AutoHotkey Community

It is currently May 27th, 2012, 8:10 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: January 1st, 2012, 10:18 pm 
Offline
User avatar

Joined: May 28th, 2011, 9:03 am
Posts: 466
Location: Germany
2012-01-03: New version!

Old wine in a new wineskin.

The following script is a proof of concept for ListView in-cell editing using the ListView's own Edit control. All functions are based on the ListViews HWNDs, so the HWND/name/number of the GUI or the ListView's vName are irrelevant. All you need to do is to call LVEDIT_INIT for each ListView passing its HWND. The script has been tested "reasonably" with Win XP, Win Vista (U32) and Win 7 (U64).

As documented ListViews don't expect to edit another column than column 1. So they blank out the text in column 1 whenever another column is edited. To avoid this, you may add an invisible column 1 as shown in the second ListView in the sample script.

LVEDIT.ahk:
Code:
; ======================================================================================================================
; Namespace:      LVEDIT_
; AHK version:    AHK 1.1.5.+ (required)
; Function:       Helper functions for in-cell ListView editing
; Language:       English
; Tested on:      Win XPSP3, Win VistaSP2 (U32) / Win 7 (U64)
; Version:        0.1.00.02/2012-01-04/just me
; ======================================================================================================================
; ----------------------------------------------------------------------------------------------------------------------
; Define LVEDIT_ as super-global
; ----------------------------------------------------------------------------------------------------------------------
Global LVEDIT_
; ----------------------------------------------------------------------------------------------------------------------
; Register ListView for in-cell editing
; Parameter:   
;     LVHWND         -  ListView's HWND
;     BlankSubItem   -  Optional: Blank out subitem's text before editing
;                       Values:  True / False
;                       Default: False
; Return values:
;     On success: True
;     On failure: False - ErrorLevel contains an additional description
; Special features:
;     LVEDIT_NOTIFY will be installed as handler for WM_NOTIFY notifications. If there is another existing handler
;     in-cell editing won't be activated.
; ----------------------------------------------------------------------------------------------------------------------
LVEDIT_INIT(LVHWND, BlankSubItem = False) {
   Static WM_NOTIFY := 0x4E
   Static SubClassProc := 0
   If !IsObject(LVEDIT_)
      LVEDIT_ := {HWND: 0, HEDIT: 0, SIL: 0, SIT: 0, SIH: 0, EditMode: False}
   MsgHandler := OnMessage(WM_NOTIFY)
   If (MsgHandler <> "") && (MsgHandler <> "LVEDIT_NOTIFY") {
      ErrorLevel := "WM_NOTIFY is already monitored!`nCell editing is not activated!"
      Return False
   }
   If !(SubClassProc) {
      If !(SubclassProc := RegisterCallback("LVEDIT_SUBCLASSPROC", "", 6)) {
         ErrorLevel := "RegisterCallback failed -> Errorlevel: " . ErrorLevel . " - A_LastError: " . A_LastError
         Return False
      }
   }
   If !DllCall("Comctl32.dll\SetWindowSubclass", "Ptr", LVHWND, "Ptr", SubclassProc, "Ptr", LVHWND, "Ptr", 0) {
      ErrorLevel := "SetWindowSubclass failed -> Errorlevel: " . ErrorLevel . " - A_LastError: " . A_LastError
      Return False
   }
   OnMessage(WM_NOTIFY, "LVEDIT_NOTIFY")
   ; Store HWND in super-global object LVEdit
   LVEDIT_[LVHWND] := BlankSubItem ? True : False
   Return True
}
; ----------------------------------------------------------------------------------------------------------------------
; ListView Subclassproc
; ----------------------------------------------------------------------------------------------------------------------
LVEDIT_SUBCLASSPROC(H, M, W, L, I, D) {
   Static EN_SETFOCUS := 0x0100
   Static EN_CHANGE := 0x0300
   Static EN_UPDATE := 0x0400
   Static EM_SETSEL := 0x00B1
   Static EM_SCROLLCARET := 0x00B7
   Static WM_COMMAND := 0x0111
   Static LVM_GETSTRINGWIDTHA := 0x1011
   Static LVM_GETSTRINGWIDTHW := 0x1057
   Static LVM_GETSTRINGWIDTH := A_IsUnicode ? LVM_GETSTRINGWIDTHW : LVM_GETSTRINGWIDTHA
   Critical
   If (LVEDIT_.EditMode) && (H = LVEDIT_.HWND) && (M = WM_COMMAND) && (L = LVEDIT_.HEDIT) {
      N := (W >> 16)
      If (N = EN_CHANGE) || (N = EN_SETFOCUS) {
         ControlGetText, EDITTEXT, , % "ahk_id " . LVEDIT_.HEDIT
         SendMessage, LVM_GETSTRINGWIDTH, 0, &EDITTEXT, , % "ahk_id " . H
         EW := ErrorLevel + 15
         EX := LVEDIT_.SIL + 6
         EY := LVEDIT_.SIT + 2
         EH := LVEDIT_.SIH
         ControlMove, , EX, EY, EW, EH, % "ahk_id " . LVEDIT_.HEDIT
         ; PostMessage, EM_SETSEL, -2, -1, , % "ahk_id " . LV.HEDIT
      }
   }
   Return DllCall("Comctl32.dll\DefSubclassProc", "Ptr", H, "UInt", M, "Ptr", W, "Ptr", L)
}
; ----------------------------------------------------------------------------------------------------------------------
; Handler for ListView notifications
; ----------------------------------------------------------------------------------------------------------------------
LVEDIT_NOTIFY(W, L) {
   Static ITEM := -1
   Static SITEM := -1
   Static ITEMTEXT := ""
   Static LVIF_TEXT := 0x01
   Static LVM_EDITLABELA := 0x1017
   Static LVM_EDITLABELW := 0x1076
   Static LVM_EDITLABEL := A_IsUnicode ? LVM_EDITLABELW : LVM_EDITLABELA
   Static LVM_SETITEMA := 0x1006
   Static LVM_SETITEMW := 0x104C
   Static LVM_SETITEM := A_IsUnicode ? LVM_SETITEMW : LVM_SETITEMA
   Static LVM_GETEDITCONTROL := 0x1018
   Static LVM_GETSUBITEMRECT := 0x1038
   Static LVN_BEGINLABELEDITA := -105
   Static LVN_BEGINLABELEDITW := -175
   Static LVN_BEGINLABELEDIT := A_IsUnicode ? LVN_BEGINLABELEDITW : LVN_BEGINLABELEDITA
   Static LVN_ENDLABELEDITA := -106
   Static LVN_ENDLABELEDITW := -176
   Static LVN_ENDLABELEDIT := A_IsUnicode ? LVN_ENDLABELEDITW : LVN_ENDLABELEDITA
   Static NM_CLICK := -2
   Static NM_DBLCLICK := -3
   Static NOTIFICATIONS := {(NM_CLICK): 1, (NM_DBLCLICK): 1, (LVN_BEGINLABELEDIT): 1, (LVN_ENDLABELEDIT): 1}
   Static LVITEMSize := (13 * 4) + (A_PtrSize * 2) + (A_PtrSize - 4) ; Size off LVITEM
   Static NMHDRSize := (2 * A_PtrSize) + 4 + (A_PtrSize - 4)         ; Size off NMHDR structure
   Static ITEMTextP := (5 * 4) + (A_PtrSize - 4)                     ; Offset of pszText in LVITEM
   Static ITEMTextL := ITEMTextP + A_PtrSize                         ; Offset of cchTextMax in LVITEM
   Critical
   H := NumGet(L + 0, 0, "UPtr")
   M := NumGet(L + (A_PtrSize * 2), 0, "Int")
   If (LVEDIT_.HasKey(H) && NOTIFICATIONS.HasKey(M)) {
      If (LVEDIT_.EditMode) {
         ; EndLabelEdit ------------------------------------------------------------------------------------------------
         If (M = LVN_ENDLABELEDIT) {
            If NumGet(L + NMHDRSize, ITEMTextP, "Ptr") {
               Numput(SITEM, L + NMHDRSize, 8, "Int")
            } Else {             ; LABELEDIT was cancelled
               If LVEDIT_[H] {   ; Subitem was blanked out -> restore subitem's text
                  VarSetCapacity(LVITEM, LVITEMSize, 0)
                  NumPut(LVIF_TEXT, LVITEM, 0, "UInt")
                  NumPut(ITEM, LVITEM, 4, "Int")
                  NumPut(SITEM, LVITEM, 8, "Int")
                  NumPut(&ITEMTEXT, LVITEM, ITEMTextP, "Ptr")
                  SendMessage, LVM_SETITEM, 0, &LVITEM, , % "ahk_id " . LVEDIT_.HWND
               }
            }
            ITEM := -1
            SITEM := -1
            ITEMTEXT := ""
            LVEDIT_.HWND := 0
            LVEDIT_.HEDIT := 0
            LVEDIT_.SIL := 0
            LVEDIT_.SIT := 0
            LVEDIT_.SIH := 0
            LVEDIT_.EditMode := False
            Return True
         }
      } Else {
         ; Single click ------------------------------------------------------------------------------------------------
         If (M = NM_CLICK) {
            ITEM := NumGet(L + NMHDRSize, 0, "Int")
            SITEM := NumGet(L + NMHDRSize, 4, "Int")
            If !(ITEM >= 0 && SITEM >= 0)
               ITEM := -1, SITEM := -1
         ; Double click ------------------------------------------------------------------------------------------------
         } Else If (M = NM_DBLCLICK) {
            ITEM := NumGet(L + NMHDRSize, 0, "Int")
            SITEM := NumGet(L + NMHDRSize, 4, "Int")
            If (ITEM >= 0 && SITEM >= 0)
               PostMessage, LVM_EDITLABEL, ITEM, 0, , % "ahk_id " . H
            Else
               ITEM := -1, SITEM := -1
         ; BeginLabelEdit ----------------------------------------------------------------------------------------------
         } Else If (M = LVN_BEGINLABELEDIT) {
            LVEDIT_.HWND := H
            ControlGetPos, LX, LY, , , , % "ahk_id " . H
            SendMessage, LVM_GETEDITCONTROL, 0, 0, , % "ahk_id " . H
            LVEDIT_.HEDIT := ErrorLevel
            ControlGet, Columns, List, Selected, , % "ahk_id " . H
            StringSplit, Column, Columns, %A_Tab%, `n
            I := SITEM + 1
            ITEMTEXT := Column%I%
            If (SITEM > 0) {
               ControlSetText, , % ITEMTEXT, % "ahk_id " . LVEDIT_.HEDIT
               If LVEDIT_[H] {   ; Subitem has to be blanked out
                  VarSetCapacity(LVITEM, LVITEMSize, 0)
                  NumPut(LVIF_TEXT, LVITEM, 0, "UInt")
                  NumPut(ITEM, LVITEM, 4, "Int")
                  NumPut(SITEM, LVITEM, 8, "Int")
                  SendMessage, LVM_SETITEM, 0, &LVITEM, , % "ahk_id " . LVEDIT_.HWND
               }
            }
            VarSetCapacity(RECT, 16, 0)
            NumPut(SITEM, RECT, 4, "Int")
            SendMessage, LVM_GETSUBITEMRECT, ITEM, &RECT, , % "ahk_id " . H
            LVEDIT_.SIL := NumGet(RECT, 0, "Int") + LX
            LVEDIT_.SIT := NumGet(RECT, 4, "Int") + LY
            LVEDIT_.SIH := NumGet(RECT, 12, "Int") - NumGet(RECT, 4, "Int")
            LVEDIT_.EditMode := True
            Return False
         }
      }
   }
}

Sample.ahk:
Code:
; ======================================================================================================================
; AHK 1.1.05+
; ======================================================================================================================
#NoEnv
#Include LVEDIT.ahk
SetBatchLines, -1
LV := "
(
123|www.google.com|Row 1
147|msdn.microsoft.com|Row 2
234|www.autohotkey.com|Row 3
288|de.autohotkey.com|Row 4
)"
; ----------------------------------------------------------------------------------------------------------------------
Gui, Margin, 20, 20
Gui, Font, s10
Gui, Add, Text, , Common ListView
Gui, Add, ListView, xm y+5 w410 -Readonly Grid r6 gMyListView hwndHLV1, Col 1|Col 2|Col 3
Loop, Parse, LV, `n
{
   If (A_LoopField) {
      StringSplit, F, A_LoopField, |
      LV_Add("", F1, F2, F3)
   }
}
Loop, 3
   LV_ModifyCol(A_Index, "200")
Gui, Add, Text, , ListView with hidden Column 1
Gui, Add, ListView, xm y+5 w410 -Readonly Grid r6 gMyListView hwndHLV2, |Col 1|Col 2|Col 3
Loop, Parse, LV, `n
{
   If (A_LoopField) {
      StringSplit, F, A_LoopField, |
      LV_Add("", "", F1, F2, F3)
   }
}
LV_ModifyCol(1, 0)
Loop, 3
   LV_ModifyCol(A_Index + 1, "200")
If !(LVEDIT_INIT(HLV1))
   MsgBox, %ErrorLevel%
If !(LVEDIT_INIT(HLV2, True))
   MsgBox, %ErrorLevel%
Gui, Show, , In-Cell ListView Editing with Doubleclick
Return
; ----------------------------------------------------------------------------------------------------------------------
GuiClose:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
MyListView:
Return

2012-01-05: Update!
  • Added: Optional parameter NIMDA for LVEDIT_INIT(). If set to True the subitem will be blanked out during editing.
  • Changed: Script and sample.

Download: :arrow: LVEDIT.ahk


Last edited by just me on January 5th, 2012, 7:05 am, edited 3 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 1st, 2012, 10:42 pm 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Very nice. Thanks! :)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 2nd, 2012, 2:19 pm 
Offline

Joined: July 31st, 2008, 10:27 pm
Posts: 336
Requires AHK_L?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 2nd, 2012, 4:49 pm 
Offline

Joined: December 26th, 2010, 7:40 pm
Posts: 4172
Location: Awesometown, USA
Morpheus wrote:
Requires AHK_L?
The Script wrote:
; ======================================================================================================================
; AHK 1.1.05+
; ======================================================================================================================


@OP
Image
The way Explorer avoids this is to "blank" the LV column immediately before or after the Edit is created.

_________________
Autofire, AutoClick, Toggle, SpamWindow Control Tools
Recommended: AutoHotkey_L


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 2nd, 2012, 5:07 pm 
Offline
User avatar

Joined: May 28th, 2011, 9:03 am
Posts: 466
Location: Germany
I've found some "noobish" bugs and I'm still fixing the script. I have to do some testing on Vista and Win 7 also, but can't do it at the moment. I hope to be able to release a lot more stable version tomorrow.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 2nd, 2012, 5:59 pm 
Offline

Joined: July 31st, 2008, 10:27 pm
Posts: 336
nimda wrote:
Morpheus wrote:
Requires AHK_L?
The Script wrote:
; ======================================================================================================================
; AHK 1.1.05+
; ======================================================================================================================


So is that a yes? :lol:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 2nd, 2012, 6:35 pm 
Offline

Joined: June 28th, 2007, 1:08 am
Posts: 662
Yep, thats a yes :P

_________________
RECOMMENDED: AutoHotkey_L
Some of the code I write will not work without it.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 3rd, 2012, 7:56 am 
Offline
User avatar

Joined: May 28th, 2011, 9:03 am
Posts: 466
Location: Germany
Done, I hope I've found and fixed all my stupid mistakes and released a (hopefully) more stable version in the OP.

Nimda wrote:
The way Explorer avoids this is to "blank" the LV column immediately before or after the Edit is created.
Probably this could be done programmatically for other colums too, but it would not prevent the ListView from blanking out the first column. I started this project some time ago and found no way to reshow the content of column 1 still. And, personally, I like to see the previous content while editing.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 4th, 2012, 1:04 am 
Offline

Joined: December 26th, 2010, 7:40 pm
Posts: 4172
Location: Awesometown, USA
just me wrote:
And, personally, I like to see the previous content while editing.
I think it looks ugly :?
The way Explorer is set up (which is the way everyone will expect it to be) makes it look like the listview cell itself is being edited. You can see the previous content; it is selected in the beginning.

Why can't you set the cell to A_Space to "blank" it?

_________________
Autofire, AutoClick, Toggle, SpamWindow Control Tools
Recommended: AutoHotkey_L


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 4th, 2012, 1:35 am 
nimda wrote:
just me wrote:
And, personally, I like to see the previous content while editing.
I think it looks ugly :?
Maybe to add BlankOnEdit property/option to this class? :P


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: January 4th, 2012, 2:48 am 
Offline

Joined: December 26th, 2010, 7:40 pm
Posts: 4172
Location: Awesometown, USA
that would work, as long as it's "unblanked" on cancel :idea:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 4th, 2012, 9:29 am 
See also Majkinetor's ComboX:

http://www.autohotkey.com/forum/topic22390-15.html

Image


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: January 4th, 2012, 11:03 am 
Offline
User avatar

Joined: May 28th, 2011, 9:03 am
Posts: 466
Location: Germany
nimda wrote:
Why can't you set the cell to A_Space to "blank" it?
I didn't say I can't.

Anonymous wrote:
See also Majkinetor's ComboX
majkinetor is "faking" in-cell editing using an external Edit control.

I'll think about blanking. :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 4th, 2012, 3:31 pm 
Offline
User avatar

Joined: May 10th, 2007, 10:54 am
Posts: 649
Location: .switzerland
Quote:
majkinetor is "faking" in-cell editing using an external Edit control.

The internal implementation of the LV does it the same way, it is even possible to make the "Edit" out of sync with the LV. I agree that this is way ugly, but any other solution would require a 3th party control.

For nice Multi-Cell Editing, the System LV lacks couple of features.

_________________
http://securityvision.ch
AHK 2D GAME ENGINE


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 5th, 2012, 7:07 am 
Offline
User avatar

Joined: May 28th, 2011, 9:03 am
Posts: 466
Location: Germany
@nimda: Well, I've thought about blanking and did it for you. :wink:


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: No registered users and 1 guest


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group