Page 1 of 2

AllowLVEdits 2.10 Beta

Posted: Tue Jun 19, 2007 11:45 pm
by netmaestro
Some work has been done on this library, specifically keyboard control is added to the editable cells, along with auto-scrolling. Arrow keys save the edit and move to the next cell in the direction.

Code: Select all

;=========================================================
; Library:           AllowLVEdits 2.10
; Author:            netmaestro  
; Date:              June 19, 2007
; Target OS:         Microsoft Windows All
; Target Compiler:   PureBasic version 4.0+
; License:           Free, unrestricted, no warranty
;==========================================================

#LVM_GETSUBITEMRECT = #LVM_FIRST + 56
#LVM_SUBITEMHITTEST = #LVM_FIRST + 57
#LVM_GETHEADER      = #LVM_FIRST + 31

Global _edits210_oldlist,_edits210_EditActive, _edits210_oldstring, _edits210_string, _edits210_decimal, _edits210_container
Global _edits210_itemrect.RECT, _edits210_window, _edits210_item, _edits210_subitem, _edits210_gadget
Global Dim MoneyColumn(100)
Global Dim Masked(100)
Declare.s GetNewText()

Procedure SaveAndExit()
  tmp$ = GetGadgetText(_edits210_string)
  crlf = FindString(tmp$, #CRLF$, 1)
  If crlf
    SetGadgetText(_edits210_string,ReplaceString(tmp$,#CRLF$,""))
    SendMessage_(sg,#EM_SETSEL,crlf-1,crlf-1)
  EndIf 
  If Moneycolumn(_edits210_subitem)
    tmpout.s  = Trim(GetGadgetText(_edits210_string))
    ploc = FindString(tmpout,Chr(_edits210_decimal),1)
    If ploc
      If ploc = 1
        tmpout = "0" + tmpout
      EndIf
      While Len(tmpout) - FindString(tmpout,Chr(_edits210_decimal),1) < 2
        tmpout+"0"
        SetGadgetText(_edits210_string, tmpout)
      Wend   
    Else
      If tmpout <> ""
        tmpout + Chr(_edits210_decimal) + "00"
        SetGadgetText(_edits210_string, tmpout)
      EndIf
    EndIf
    SetGadgetItemText(_edits210_gadget,_edits210_item, RSet(Trim(tmpout),Moneycolumn(_edits210_subitem)+3," "), _edits210_subitem)
  Else
    SetGadgetItemText(_edits210_gadget,_edits210_item, GetGadgetText(_edits210_string), _edits210_subitem)
  EndIf
  FreeGadget(_edits210_string)
  FreeGadget(_edits210_container)
  _edits210_EditActive = #False
EndProcedure

Procedure SubClass_String(hwnd, Message, wparam, lparam) 
  Protected blocking=0, ploc, tmpout.s
  result = CallWindowProc_(_edits210_oldlist, hwnd, Message, wparam, lparam) 
  Select Message

    Case #WM_KEYDOWN
       Select wparam
       
        Case 37
          SendMessage_(hwnd, #EM_GETSEL, @first.l, @last.l)
          If first = 0
            SaveAndExit()
            currentsubitem = _edits210_subitem
            _edits210_subitem - 1 
            If _edits210_subitem >= 1
              While masked(_edits210_subitem) And _edits210_subitem >= 0
                _edits210_subitem - 1
                If _edits210_subitem < 1
                  _edits210_subitem = currentsubitem
                EndIf
              Wend
            Else
              _edits210_subitem + 1
            EndIf
            RtlZeroMemory_(@_edits210_itemrect,SizeOf(RECT))
            _edits210_itemrect\top = _edits210_subitem
            SendMessage_(GadgetID(_edits210_gadget), #LVM_GETSUBITEMRECT, _edits210_item, @_edits210_itemrect)
            GetNewText()
          EndIf
          
        Case 39
          SendMessage_(hwnd, #EM_GETSEL, @first.l, @last.l)
          If Len(GetGadgetText(_edits210_string)) = last
            headerhWnd=SendMessage_(GadgetID(_edits210_gadget),#LVM_GETHEADER,0,0) 
            numcolumns = SendMessage_(headerhWnd, #HDM_GETITEMCOUNT,0,0)
            SaveAndExit()
            currentsubitem = _edits210_subitem
            _edits210_subitem + 1 
            If _edits210_subitem < numcolumns
              While masked(_edits210_subitem) And _edits210_subitem <= numcolumns
                _edits210_subitem + 1
                If _edits210_subitem >= numcolumns
                  _edits210_subitem = currentsubitem
                EndIf
              Wend
            Else
              _edits210_subitem -1
            EndIf
            RtlZeroMemory_(@_edits210_itemrect,SizeOf(RECT))
            _edits210_itemrect\top = _edits210_subitem
            SendMessage_(GadgetID(_edits210_gadget), #LVM_GETSUBITEMRECT, _edits210_item, @_edits210_itemrect)
            GetNewText()
          EndIf
          
        Case 38 
          SaveAndExit()
          _edits210_item - 1 : If _edits210_item <= 0 : _edits210_item = 0 : EndIf
          RtlZeroMemory_(@_edits210_itemrect,SizeOf(RECT))
          _edits210_itemrect\top = _edits210_subitem
          SendMessage_(GadgetID(_edits210_gadget), #LVM_GETSUBITEMRECT, _edits210_item, @_edits210_itemrect)

          GetNewText()

        Case 40 
          SaveAndExit()
          _edits210_item + 1:If _edits210_item >= CountGadgetItems(_edits210_gadget):_edits210_item - 1 : EndIf
          RtlZeroMemory_(@_edits210_itemrect,SizeOf(RECT))
          _edits210_itemrect\top = _edits210_subitem
          SendMessage_(GadgetID(_edits210_gadget), #LVM_GETSUBITEMRECT, _edits210_item, @_edits210_itemrect)
          GetNewText()
          
      EndSelect
      
    Case #WM_KEYUP  
      Select wparam
        Case 13
          SaveAndExit()
        Case 27
          FreeGadget(_edits210_string)
          FreeGadget(_edits210_container)
          _edits210_EditActive = #False
      EndSelect
      
    Case #WM_CHAR
      If MoneyColumn(_edits210_subitem)
        blocking = 1
        Select wparam
  
          Case _edits210_decimal, 48 To 57
            tmpout.s = GetGadgetText(_edits210_string)
            If FindString(tmpout, Chr(_edits210_decimal), 1) = 0
              If Len(tmpout) > MoneyColumn(_edits210_subitem)
                If wparam <> _edits210_decimal
                  SendMessage_(hwnd, #EM_GETSEL, 0, @pos) 
                  SetGadgetText(_edits210_string, Left(tmpout,Len(tmpout)-1)) 
                  SendMessage_(hwnd, #EM_SETSEL, pos-1, pos-1) 
                EndIf
              EndIf
            EndIf  
            If wparam = _edits210_decimal
              If CountString(tmpout, Chr(_edits210_decimal)) > 1
                SendMessage_(hwnd, #EM_GETSEL, 0, @pos) 
                SetGadgetText(_edits210_string, Left(tmpout,Len(tmpout)-1)) 
                SendMessage_(hwnd, #EM_SETSEL, pos-1, pos-1) 
              EndIf        
            EndIf
            If FindString(tmpout,Chr(_edits210_decimal),1)
              If Len(tmpout) - FindString(tmpout,Chr(_edits210_decimal),1) > 2
                SendMessage_(hwnd, #EM_GETSEL, 0, @pos) 
                SetGadgetText(_edits210_string, Left(tmpout,Len(tmpout)-1)) 
                SendMessage_(hwnd, #EM_SETSEL, pos-1, pos-1) 
              EndIf        
            EndIf
          Default
            If wparam <> 8 And wparam <> 13
              tmpout.s = GetGadgetText(_edits210_string)
              SendMessage_(hwnd, #EM_GETSEL, 0, @pos) 
              SetGadgetText(_edits210_string, Left(tmpout,Len(tmpout)-1)) 
              SendMessage_(hwnd, #EM_SETSEL, pos-1, pos-1) 
            EndIf
        EndSelect
      EndIf
      
    Case #WM_PASTE
      If MoneyColumn(_edits210_subitem)
        tmpout.s = GetGadgetText(_edits210_string)
        If FindString(tmpout,Chr(_edits210_decimal),1)
          validlength = MoneyColumn(_edits210_subitem)+3
        Else
          validlength = MoneyColumn(_edits210_subitem)
        EndIf
        If Len(tmpout)<=validlength
          ok = #True
          *cc.CHARACTER = @tmpout
          While *cc\c
            Select *cc\c
              Case '0' To '9', _edits210_decimal
              Default
                ok = #False
            EndSelect
            *cc+1
          Wend
        Else
          ok = #False
        EndIf
        If Not ok
          SetGadgetText(_edits210_string, "")
        EndIf
      EndIf
  EndSelect

  ProcedureReturn result 
EndProcedure 

Procedure.s GetNewText()
  _edits210_EditActive = #True
  Protected grect.rect
  twleft  =   _edits210_itemrect\left+1
  twtop   =   _edits210_itemrect\top
  winwidth =  _edits210_itemrect\right-_edits210_itemrect\left-1
  winheight = _edits210_itemrect\bottom-_edits210_itemrect\top-1
  CreateGadgetList(GadgetID(_edits210_gadget))
  _edits210_container = ContainerGadget(#PB_Any, twleft-2,twtop-2,winwidth+4,winheight+4)
  HideGadget(_edits210_container, 1)
  SetGadgetColor(_edits210_container,#PB_Gadget_BackColor, #Black)
  If MoneyColumn(_edits210_subitem)
    _edits210_string  = StringGadget(#PB_Any,2,2,winwidth,winheight,RemoveString(GetGadgetItemText(_edits210_gadget,_edits210_item,_edits210_subitem)," "),#PB_String_BorderLess|#ES_CENTER|#ES_MULTILINE|#ES_AUTOVSCROLL) 
  Else
    _edits210_string  = StringGadget(#PB_Any,2,2,winwidth,winheight,GetGadgetItemText(_edits210_gadget,_edits210_item,_edits210_subitem),#PB_String_BorderLess|#ES_CENTER|#ES_MULTILINE|#ES_AUTOVSCROLL) 
  EndIf   
  CloseGadgetList()   
  SetGadgetFont(_edits210_string, GetGadgetFont(_edits210_gadget))
  UseGadgetList(WindowID(_edits210_window))
  SetActiveGadget(_edits210_string) 
  _edits210_oldstring=SetWindowLong_(GadgetID(_edits210_string), #GWL_WNDPROC, @SubClass_String()) 
  SendMessage_(GadgetID(_edits210_string),#EM_SETSEL,0,-1)
  HideGadget(_edits210_container, 0)
  GetWindowRect_(GadgetID(_edits210_gadget), @gr.RECT)
  GetWindowRect_(GadgetID(_edits210_container), @cr.RECT)
  GetClientRect_(GadgetID(_edits210_gadget), @grc.RECT)
  If cr\top - gr\top < winheight
    SendMessage_(GadgetID(_edits210_gadget), #WM_VSCROLL, #SB_LINEUP ,0)
  ElseIf cr\top - gr\top > grc\bottom-grc\top-winheight
    SendMessage_(GadgetID(_edits210_gadget), #WM_VSCROLL, #SB_LINEDOWN ,0)
  EndIf
  If GadgetWidth(_edits210_gadget)-twleft < winwidth
    SendMessage_(GadgetID(_edits210_gadget), #WM_HSCROLL, #SB_RIGHT, 0)
  ElseIf twleft<0
    SendMessage_(GadgetID(_edits210_gadget), #WM_HSCROLL, #SB_LEFT, 0)
  EndIf
EndProcedure 

Procedure ResizeEdit(hwnd)
  RtlZeroMemory_(@_edits210_itemrect,SizeOf(RECT)) 
  _edits210_itemrect\top = _edits210_subitem
  SendMessage_(hwnd, #LVM_GETSUBITEMRECT, _edits210_item, @_edits210_itemrect)      
  twleft    = _edits210_itemrect\left+1
  twtop     = _edits210_itemrect\top
  winwidth  = _edits210_itemrect\right-_edits210_itemrect\left-1
  winheight = _edits210_itemrect\bottom-_edits210_itemrect\top-1
  ResizeGadget(_edits210_container,twleft-2,twtop-2,winwidth+4,winheight+4)
  ResizeGadget(_edits210_string, 2,2, GadgetWidth(_edits210_container)-4, GadgetHeight(_edits210_container)-4)
  InvalidateRect_(GadgetID(_edits210_container),0,1) 
  If GadgetY(_edits210_container) < winheight
    HideGadget(_edits210_container, 1)
  Else
    HideGadget(_edits210_container, 0)
    SetActiveGadget(_edits210_string)
  EndIf          
EndProcedure

Procedure SubClass_LV(hwnd, Message, wparam, lparam) 

  result = CallWindowProc_(_edits210_oldlist, hwnd, Message, wparam, lparam) 
  
  If message = #WM_KEYUP
    If wparam = #VK_TAB
      If GetAsyncKeyState_(#VK_SHIFT) & 32768
        SaveAndExit()
        currentsubitem = _edits210_subitem
        _edits210_subitem - 1 
        If _edits210_subitem >= 1
          While masked(_edits210_subitem) And _edits210_subitem >= 0
            _edits210_subitem - 1
            If _edits210_subitem < 1
              _edits210_subitem = currentsubitem
            EndIf
          Wend
        Else
          _edits210_subitem + 1
        EndIf
        RtlZeroMemory_(@_edits210_itemrect,SizeOf(RECT))
        _edits210_itemrect\top = _edits210_subitem
        SendMessage_(GadgetID(_edits210_gadget), #LVM_GETSUBITEMRECT, _edits210_item, @_edits210_itemrect)
        GetNewText()       
      Else
        If IsGadget(_edits210_string) 
          headerhWnd=SendMessage_(GadgetID(_edits210_gadget),#LVM_GETHEADER,0,0) 
          numcolumns = SendMessage_(headerhWnd, #HDM_GETITEMCOUNT,0,0)
          SaveAndExit()
          currentsubitem = _edits210_subitem
          _edits210_subitem + 1 
          If _edits210_subitem < numcolumns
            While masked(_edits210_subitem) And _edits210_subitem <= numcolumns
              _edits210_subitem + 1
              If _edits210_subitem >= numcolumns
                _edits210_subitem = currentsubitem
              EndIf
            Wend
          Else
            _edits210_subitem -1
          EndIf
          RtlZeroMemory_(@_edits210_itemrect,SizeOf(RECT))
          _edits210_itemrect\top = _edits210_subitem
          SendMessage_(GadgetID(_edits210_gadget), #LVM_GETSUBITEMRECT, _edits210_item, @_edits210_itemrect)
          GetNewText()
        EndIf
      EndIf
    EndIf
  EndIf
  
  If message = #WM_VSCROLL Or message = #WM_HSCROLL
    If IsGadget(_edits210_container) And _edits210_container <> 0
      ResizeEdit(hwnd)
    EndIf
  EndIf
   
  If message=#WM_NOTIFY 
    If IsGadget(_edits210_container) And _edits210_container <> 0
      *nmHEADER.HD_NOTIFY = lParam 
      Select *nmHEADER\hdr\code 
        Case #HDN_ITEMCHANGING 
          ResizeEdit(hwnd)
      EndSelect 
    EndIf
  EndIf
   
  If Message = #WM_RBUTTONDOWN 
    If Not _edits210_EditActive
      GetCursorPos_(@cp.POINT)
      MapWindowPoints_(0,hwnd,@cp,1)
      HitInfo.LVHITTESTINFO 
      Hitinfo\pt\x = cp\x
      HitInfo\pt\y = cp\y
      SendMessage_(hwnd,#LVM_SUBITEMHITTEST ,0,@HitInfo)
      If hitinfo\isubitem > 0 And HitInfo\iItem >= 0
        RtlZeroMemory_(@_edits210_itemrect,SizeOf(RECT))
        _edits210_itemrect\top = hitinfo\iSubItem
        SendMessage_(hwnd,#LVM_GETSUBITEMRECT, hitinfo\iitem, @_edits210_itemrect)
        _edits210_item = hitinfo\iitem
        _edits210_subitem = hitinfo\iSubItem
        If Not Masked(_edits210_subitem)
          GetNewText() 
        EndIf
      EndIf
    Else
      FreeGadget(_edits210_string)
      FreeGadget(_edits210_container)
      _edits210_EditActive = #False
    EndIf
  EndIf 
  
  If Message = #WM_LBUTTONDOWN
    If _edits210_EditActive
      FreeGadget(_edits210_string)
      FreeGadget(_edits210_container)
      _edits210_EditActive = #False
    EndIf
  EndIf 
  ProcedureReturn result 
EndProcedure 

ProcedureDLL StartEditing(WindowNumber, GadgetNumber); Make ListIcon gadget cells editable
  _edits210_window=WindowNumber
  Protected cn$ = Space(100)
  GetClassName_(GadgetID(GadgetNumber),@cn$,99)
  If UCase(Trim(cn$)) = "SYSLISTVIEW32" And _edits210_oldlist = 0
    _edits210_oldlist=SetWindowLong_(GadgetID(GadgetNumber), #GWL_WNDPROC, @SubClass_LV()) 
    _edits210_gadget = GadgetNumber
    Global Dim Masked(100)
    Global Dim MoneyColumn(100)
    _edits210_decimal = 46
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

ProcedureDLL StopEditing(GadgetNumber); Take ListIcon gadget out of editable mode
  SetWindowLong_(GadgetID(GadgetNumber), #GWL_WNDPROC, _edits210_oldlist) 
  _edits210_oldlist = 0
EndProcedure

ProcedureDLL SetMoneyColumn(Column, Width) ; Width is number of digits before the decimal place
  If _edits210_oldlist
    If width >= 1
      MoneyColumn(Column) = width
    Else
      MoneyColumn(Column) = 1
    EndIf
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

ProcedureDLL DecimalMask(char) ; Char is the character you wish to use for decimal point, usually '.' or ',' (46 or 44)
  _edits210_decimal = char
EndProcedure

ProcedureDLL MaskColumn(Column) ; Disallows edits for the passed column
  If _edits210_oldlist
    Masked(Column) = 1
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Code: Select all

;=======================================================
; Program:        AllowLVEdits 2.1 beta Demo program
; Author:         netmaestro
;=======================================================
;
IncludeFile "AllowLVEdits210.pbi"

w = OpenWindow(0,0,0,800,700,"AllowLVEdits 2.1 Demo",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
CreateGadgetList(w)

ListIconGadget(0,0,0,800,700,"",0,#PB_ListIcon_GridLines)
For i=1 To 26
  AddGadgetColumn(0,i,Chr(64+i),60)
Next

For i=1 To 50
  AddGadgetItem(0, -1, Chr(10)+RSet(Str(i),2,"0"))
Next

For i=0 To CountGadgetItems(0)-1
  SetGadgetItemColor(0,i,#PB_Gadget_FrontColor,#Black,1)
  SetGadgetItemColor(0,i,#PB_Gadget_BackColor,RGB(246,247,244),1)
Next

StartEditing(0,0)

MaskColumn(1)

For i=1 To 15
  SetMoneyColumn(i,4)
Next

Repeat:Until WaitWindowEvent()=#WM_CLOSE

StopEditing(0)

Posted: Wed Jun 20, 2007 12:04 am
by srod
Hi,

looks cool.

I just ran the demo but I can't seem to edit any cells! I mean the cell is highlighted and the caret is displayed, but I cannot seem to enter any text!

Think there's a bug. Try this. Highlight a cell. Hit tab. Hit the right arrow key a few dozen times. Now click another cell. Unless it's just my barmy machine, you should see the problem at this point.

Posted: Wed Jun 20, 2007 12:24 am
by netmaestro
Sorry, in the demo they're all set to money fields. No text. The Tab key isn't handled right now, but as it's changing the keyboard focus off the edit field, something should be done with it.

Posted: Wed Jun 20, 2007 3:00 am
by netmaestro
New version of the beta is uploaded, it treats the TAB key exactly as the right-arrow key except it doesn't check to see if the caret is at the end of the edit field. It just goes right. SHIFT-TAB behaves same as left-arrow key except it doesn't check to see if the caret is at the beginning. It just goes left. Also, if you want to enter text, the demo is only forcing currency for the columns up to O. From P on, it'll take whatever you want to put in it.

Posted: Wed Jun 20, 2007 10:47 am
by srod
That's better! :)

Only thing I can find now is that when tabbing through the cells, and when the right-hand edge of the control is encountered, the new cell is sometimes not shown. The same is true when shift-tabbing etc. Just a minor thing!

This is quite a handy little library.

Posted: Sun Dec 30, 2007 8:43 pm
by Marco2007
Hi netmaestro,

is there a chance to download it? The link above doesn`t work (for me?) :cry:

thanx
Marco

Posted: Wed Jan 02, 2008 7:19 am
by netmaestro
lib and a test prog are in a zip available in first post. I'll be releasing the source and a help.chm with this soon, but have a play with it for now and let me know of any bugs. Right-click a cell to edit it, all available commands are in the test prog and the status bar explains usage.

Posted: Wed Jan 02, 2008 5:44 pm
by Marco2007
Thank you very much, netmaestro!

Posted: Sat Jan 05, 2008 10:27 am
by Marco2007
Hi netmaestro,

I can`t edit column 0! I don`t know, if it`s mentioned somewhere...

Code: Select all

Enumeration
  #Window
EndEnumeration

Enumeration
  #ListIcon
EndEnumeration


Procedure Open_Window()
  If OpenWindow(#Window, 216, 0, 295, 182, "Win",  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar )
    If CreateGadgetList(WindowID(#Window))
      
      ;-
      ListIconGadget(#ListIcon, 25, 30, 250, 115, "Col0", 100, #PB_ListIcon_GridLines)
      AddGadgetColumn(#ListIcon, 1, "Col1", 100)
      AddGadgetItem(#listicon, -1, "test"+Chr(10)+"test")
      AddGadgetItem(#listicon, -1, "test"+Chr(10)+"test")
    EndIf
  EndIf
EndProcedure

Open_Window()
StartEditing(#Window,#ListIcon)

Repeat:Until WaitWindowEvent()=#WM_CLOSE

StopEditing(#ListIcon)


AllowLVedits and ListIcon FillByGadget don`t chime together... :(

Marco

Posted: Sat Jan 05, 2008 1:53 pm
by nicolaus
netmaestro wrote:lib and a test prog are in a zip available in first post. I'll be releasing the source and a help.chm with this soon, but have a play with it for now and let me know of any bugs. Right-click a cell to edit it, all available commands are in the test prog and the status bar explains usage.
Wat for a PB version i must use? I have test it with PB 4.02 but the democode from your zip crash with polinkerror

Posted: Sat Jan 05, 2008 2:19 pm
by Marco2007
...works with PB4.10

Posted: Sat Jan 05, 2008 3:08 pm
by netmaestro
Column 0 is not designed to be editable. It's the label/icon column anyway, if you want all columns to be editable don't use it. Just start at column 1 in your gadget.

Posted: Sun Apr 06, 2008 4:56 am
by Rook Zimbabwe
Downloaded zip... source code ok, I see the other file, but it has no designator... is it a LIB? What is the file extension? 8)

Posted: Sun Apr 06, 2008 5:45 am
by netmaestro
PureBasic user libraries have no file extension. It goes in Libraries\Userlibraries and then restart the compiler and you've installed it.

Posted: Sun Apr 06, 2008 8:52 am
by graves
Marco2007 wrote:...works with PB4.10
I'm using 4.10 and have a POLINK error too...

Code: Select all

PBCompiler "C:\Aplicaciones\PureBasic\allowEdit\demo21.PB" /exe "C:\Aplicaciones\PureBasic\allowEdit\demo21.exe"

******************************************
PureBasic 4.10 (Windows - x86)
******************************************

Compiling C:\Aplicaciones\PureBasic\allowEdit\demo21.PB
Loading external libraries...
Starting compilation...
35 lines processed.
Creating executable.
Error: Linker
POLINK: error: Unresolved external symbol '_PB_StringBasePosition'.
POLINK: fatal error: 1 unresolved external(s).
EDIT:
POLINK error only with "/thread" option.