├── .gitattributes ├── .gitignore ├── README.md └── Sources ├── Class_LV_InCellEdit.ahk └── Sample.ahk /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Class_LV_InCellEdit # 2 | 3 | AHK class providing in-cell editing for ListView controls in AHK GUIs. 4 | 5 | The class provides methods to restrict editing to certain columns, to directly start editing of a specified cell, 6 | and to deactivate/activate the built-in message handler for `WM_NOTIFY` messages (see below). 7 | 8 | The message handler for `WM_NOTIFY` messages will be activated for the specified ListView whenever a new instance is 9 | created. As long as the message handler is activated a double-click on any cell will show an Edit control within this 10 | cell allowing to edit the current content. The default behavior for editing the first column by two subsequent single 11 | clicks is disabled. You have to press "Esc" to cancel editing, otherwise the content of the Edit will be stored in 12 | the current cell. ListViews must have the `-ReadOnly` option to be editable. 13 | 14 | While editing, "Esc", "Tab", "Shift+Tab", "Down", and "Up" keys are registered as hotkeys. "Esc" will cancel editing 15 | without changing the value of the current cell. All other hotkeys will store the content of the edit in the current 16 | cell and continue editing for the next (Tab), previous (Shift+Tab), upper (Up), or lower (Down) cell. You cannot use 17 | the keys for other purposes while editing. 18 | 19 | All changes are stored in `MyInstance.Changed`. You may track the changes by triggering `(A_GuiEvent == "F")` in the 20 | ListView's gLabel and checking `MyInstance["Changed"]` as shown in the sample scipt. If "True", `MyInstance.Changed` 21 | contains an array of objects with keys "Row" (row number), "Col" (column number), and "Txt" (new content). 22 | 'Changed' is one of the two keys intended to be accessed directly from outside the class. 23 | 24 | If you want to temporarily disable in-cell editing call `MyInstance.OnMessage(False)`. This must be done also before 25 | you try to destroy the instance. To enable it again, call `MyInstance.OnMessage()``. 26 | 27 | To avoid the loss of Gui events and messages the message handler might need to be `Critical`. This can be 28 | achieved by setting the instance property 'Critical' to the required value (e.g. `MyInstance.Critical := 100`). 29 | New instances default to `Critical, Off`. Though sometimes needed, ListViews or the whole Gui may become 30 | unresponsive under certain circumstances if `Critical` is set and the ListView has a g-label. 31 | 32 | 33 | -------------------------------------------------------------------------------- /Sources/Class_LV_InCellEdit.ahk: -------------------------------------------------------------------------------- 1 | ; ====================================================================================================================== 2 | ; Namespace: LV_InCellEdit 3 | ; Function: Support for in-cell ListView editing. 4 | ; Tested with: AHK 1.1.22.09 (1.1.20+ required) 5 | ; Tested on: Win 10 Pro (x64) 6 | ; Change History: 1.2.02.00/2015-12-14/just me - Bug fix and support for centered columns. 7 | ; 1.2.01.00/2015-09-08/just me - Added EditUserFunc option. 8 | ; 1.2.00.00/2015-03-29/just me - New version based on AHK 1.1.20+ features. 9 | ; 1.1.04.00/2014-03-22/just me - Added method EditCell 10 | ; 1.1.03.00/2012-05-05/just me - Added back option BlankSubItem for method Attach 11 | ; 1.1.02.00/2012-05-01/just me - Added method SetColumns 12 | ; 1.1.01.00/2012-03-18/just me 13 | ; ====================================================================================================================== 14 | ; CLASS LV_InCellEdit 15 | ; 16 | ; Unlike other in-cell editing scripts, this class is using the ListViews built-in edit control. 17 | ; Advantage: 18 | ; You don't have to care about the font and the GUI, and most of the job can be done by handling common ListView 19 | ; notifications. 20 | ; Disadvantage: 21 | ; I've still found no way to prevent the ListView from blanking out the first subitem of the row while editing 22 | ; another subitem. The only known workaround is to add a hidden first column. 23 | ; 24 | ; The class provides methods to restrict editing to certain columns, to directly start editing of a specified cell, 25 | ; and to deactivate/activate the built-in message handler for WM_NOTIFY messages (see below). 26 | ; 27 | ; The message handler for WM_NOTIFY messages will be activated for the specified ListView whenever a new instance is 28 | ; created. As long as the message handler is activated a double-click on any cell will show an Edit control within this 29 | ; cell allowing to edit the current content. The default behavior for editing the first column by two subsequent single 30 | ; clicks is disabled. You have to press "Esc" to cancel editing, otherwise the content of the Edit will be stored in 31 | ; the current cell. ListViews must have the -ReadOnly option to be editable. 32 | ; 33 | ; While editing, "Esc", "Tab", "Shift+Tab", "Down", and "Up" keys are registered as hotkeys. "Esc" will cancel editing 34 | ; without changing the value of the current cell. All other hotkeys will store the content of the edit in the current 35 | ; cell and continue editing for the next (Tab), previous (Shift+Tab), upper (Up), or lower (Down) cell. You cannot use 36 | ; the keys for other purposes while editing. 37 | ; 38 | ; All changes are stored in MyInstance.Changed. You may track the changes by triggering (A_GuiEvent == "F") in the 39 | ; ListView's gLabel and checking MyInstance["Changed"] as shown in the sample scipt. If "True", MyInstance.Changed 40 | ; contains an array of objects with keys "Row" (row number), "Col" (column number), and "Txt" (new content). 41 | ; Changed is one of the two keys intended to be accessed directly from outside the class. 42 | ; 43 | ; If you want to temporarily disable in-cell editing call MyInstance.OnMessage(False). This must be done also before 44 | ; you try to destroy the instance. To enable it again, call MyInstance.OnMessage(). 45 | ; 46 | ; To avoid the loss of Gui events and messages the message handler might need to be 'critical'. This can be 47 | ; achieved by setting the instance property 'Critical' to the required value (e.g. MyInstance.Critical := 100). 48 | ; New instances default to 'Critical, Off'. Though sometimes needed, ListViews or the whole Gui may become 49 | ; unresponsive under certain circumstances if Critical is set and the ListView has a g-label. 50 | ; ====================================================================================================================== 51 | Class LV_InCellEdit { 52 | ; Instance properties ----------------------------------------------------------------------------------------------- 53 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 54 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 55 | ; META FUNCTIONS ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 56 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 57 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 58 | ; =================================================================================================================== 59 | ; __New() Creates a new LV_InCellEdit instance for the specified ListView. 60 | ; Parameters: HWND - ListView's HWND 61 | ; Optional ------------------------------------------------------------------------------------------ 62 | ; HiddenCol1 - ListView with hidden first column 63 | ; Values: True / False 64 | ; Default: False 65 | ; BlankSubItem - Blank out subitem's text while editing 66 | ; Values: True / False 67 | ; Default: False 68 | ; EditUserFunc - The name of a user-defined funtion to be called from 69 | ; LVN_BEGINEDITLABEL and LVN_ENDEDITLABEL. 70 | ; The function must accept at least 6 Parameters: 71 | ; State - The state of the edit operation: BEGIN / END 72 | ; HLV - The handle to the ListView. 73 | ; HED - The handle to the edit control. 74 | ; Row - The row number of the edited item. 75 | ; Col - The column number of the edited item. 76 | ; Text - The edited item's text before / after editing. 77 | ; To avoid the loss of messages the function should return as soon as possible. 78 | ; =================================================================================================================== 79 | __New(HWND, HiddenCol1 := False, BlankSubItem := False, EditUserFunc := "") { 80 | If (This.Base.Base.__Class) ; do not instantiate instances 81 | Return False 82 | If This.Attached[HWND] ; HWND is already attached 83 | Return False 84 | If !DllCall("IsWindow", "Ptr", HWND) ; invalid HWND 85 | Return False 86 | VarSetCapacity(Class, 512, 0) 87 | DllCall("GetClassName", "Ptr", HWND, "Str", Class, "Int", 256) 88 | If (Class <> "SysListView32") ; HWND doesn't belong to a ListView 89 | Return False 90 | If (EditUserFunc <> "") && (Func(EditUserFunc).MaxParams < 6) 91 | Return False 92 | ; ---------------------------------------------------------------------------------------------------------------- 93 | ; Set LVS_EX_DOUBLEBUFFER (0x010000) style to avoid drawing issues. 94 | SendMessage, 0x1036, 0x010000, 0x010000, , % "ahk_id " . HWND ; LVM_SETEXTENDEDLISTVIEWSTYLE 95 | This.HWND := HWND 96 | This.HEDIT := 0 97 | This.Item := -1 98 | This.SubItem := -1 99 | This.ItemText := "" 100 | This.RowCount := 0 101 | This.ColCount := 0 102 | This.Cancelled := False 103 | This.Next := False 104 | This.Skip0 := !!HiddenCol1 105 | This.Blank := !!BlankSubItem 106 | This.Critical := "Off" 107 | This.DW := 0 108 | This.EX := 0 109 | This.EY := 0 110 | This.EW := 0 111 | This.EH := 0 112 | This.LX := 0 113 | This.LY := 0 114 | This.LR := 0 115 | This.LW := 0 116 | This.SW := 0 117 | If (EditUserFunc <> "") 118 | This.EditUserFunc := Func(EditUserFunc) 119 | This.OnMessage() 120 | This.Attached[HWND] := True 121 | } 122 | ; =================================================================================================================== 123 | __Delete() { 124 | This.Attached.Remove(This.HWND, "") 125 | WinSet, Redraw, , % "ahk_id " . This.HWND 126 | } 127 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 128 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 129 | ; PUBLIC INTERFACE ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 130 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 131 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 132 | ; =================================================================================================================== 133 | ; EditCell Edit the specified cell, if possible. 134 | ; Parameters: Row - 1-based row number 135 | ; Col - 1-based column number 136 | ; Default: 0 - edit the first editable column 137 | ; Return values: True on success; otherwise False 138 | ; =================================================================================================================== 139 | EditCell(Row, Col := 0) { 140 | If !This.HWND 141 | Return False 142 | ControlGet, Rows, List, Count, , % "ahk_id " . This.HWND 143 | This.RowCount := Rows - 1 144 | ControlGet, ColCount, List, Count Col, , % "ahk_id " . This.HWND 145 | This.ColCount := ColCount - 1 146 | If (Col = 0) { 147 | If (This["Columns"]) 148 | Col := This.Columns.MinIndex() + 1 149 | ELse If This.Skip0 150 | Col := 2 151 | Else 152 | Col := 1 153 | } 154 | If (Row < 1) || (Row > Rows) || (Col < 1) || (Col > ColCount) 155 | Return False 156 | If (Column = 1) && This.Skip0 157 | Col := 2 158 | If (This["Columns"]) 159 | If !This.Columns[Col - 1] 160 | Return False 161 | VarSetCapacity(LPARAM, 1024, 0) 162 | NumPut(Row - 1, LPARAM, (A_PtrSize * 3) + 0, "Int") 163 | NumPut(Col - 1, LPARAM, (A_PtrSize * 3) + 4, "Int") 164 | This.NM_DBLCLICK(&LPARAM) 165 | Return True 166 | } 167 | ; =================================================================================================================== 168 | ; SetColumns Sets the columns you want to edit 169 | ; Parameters: ColNumbers* - zero or more numbers of column which shall be editable. If entirely omitted, 170 | ; the ListView will be reset to enable editing of all columns. 171 | ; Return values: True on success; otherwise False 172 | ; =================================================================================================================== 173 | SetColumns(ColNumbers*) { 174 | If !This.HWND 175 | Return False 176 | This.Remove("Columns") 177 | If (ColNumbers.MinIndex() = "") 178 | Return True 179 | ControlGet, ColCount, List, Count Col, , % "ahk_id " . This.HWND 180 | Indices := [] 181 | For Each, Col In ColNumbers { 182 | If Col Is Not Integer 183 | Return False 184 | If (Col < 1) || (Col > ColCount) 185 | Return False 186 | Indices[Col - 1] := True 187 | } 188 | This["Columns"] := Indices 189 | Return True 190 | } 191 | ; =================================================================================================================== 192 | ; OnMessage Activate / deactivate the message handler for WM_NOTIFY messages for this ListView 193 | ; Parameters: Apply - True / False 194 | ; Default: True 195 | ; Return Value: Always True 196 | ; =================================================================================================================== 197 | OnMessage(Apply := True) { 198 | If !This.HWND 199 | Return False 200 | If (Apply) && !This.HasKey("NotifyFunc") { 201 | This.NotifyFunc := ObjBindMethod(This, "On_WM_NOTIFY") 202 | OnMessage(0x004E, This.NotifyFunc) ; add the WM_NOTIFY message handler 203 | } 204 | Else If !(Apply) && This.HasKey("NotifyFunc") { 205 | OnMessage(0x004E, This.NotifyFunc, 0) ; remove the WM_NOTIFY message handler 206 | This.NotifyFunc := "" 207 | This.Remove("NotifyFunc") 208 | } 209 | WinSet, Redraw, , % "ahk_id " . This.HWND 210 | Return True 211 | } 212 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 213 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 214 | ; PRIVATE PROPERTIES ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 215 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 216 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 217 | ; Class properties -------------------------------------------------------------------------------------------------- 218 | Static Attached := {} 219 | Static OSVersion := DllCall("GetVersion", "UChar") 220 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 221 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 222 | ; PRIVATE METHODS +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 223 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 224 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 225 | ; ------------------------------------------------------------------------------------------------------------------- 226 | ; WM_COMMAND message handler for edit notifications 227 | ; ------------------------------------------------------------------------------------------------------------------- 228 | On_WM_COMMAND(W, L, M, H) { 229 | ; LVM_GETSTRINGWIDTHW = 0x1057, LVM_GETSTRINGWIDTHA = 0x1011 230 | Critical, % This.Critical 231 | If (L = This.HEDIT) { 232 | N := (W >> 16) 233 | If (N = 0x0400) || (N = 0x0300) || (N = 0x0100) { ; EN_UPDATE | EN_CHANGE | EN_SETFOCUS 234 | If (N = 0x0100) ; EN_SETFOCUS 235 | SendMessage, 0x00D3, 0x01, 0, , % "ahk_id " . L ; EM_SETMARGINS, EC_LEFTMARGIN 236 | ControlGetText, EditText, , % "ahk_id " . L 237 | SendMessage, % (A_IsUnicode ? 0x1057 : 0x1011), 0, % &EditText, , % "ahk_id " . This.HWND 238 | EW := ErrorLevel + This.DW 239 | , EX := This.EX 240 | , EY := This.EY 241 | , EH := This.EH + (This.OSVersion < 6 ? 3 : 0) ; add 3 for WinXP 242 | If (EW < This.MinW) 243 | EW := This.MinW 244 | If (EX + EW) > This.LR 245 | EW := This.LR - EX 246 | DllCall("SetWindowPos", "Ptr", L, "Ptr", 0, "Int", EX, "Int", EY, "Int", EW, "Int", EH, "UInt", 0x04) 247 | If (N = 0x0400) ; EN_UPDATE 248 | Return 0 249 | } 250 | } 251 | } 252 | ; ------------------------------------------------------------------------------------------------------------------- 253 | ; WM_HOTKEY message handler 254 | ; ------------------------------------------------------------------------------------------------------------------- 255 | On_WM_HOTKEY(W, L, M, H) { 256 | ; LVM_CANCELEDITLABEL = 0x10B3, Hotkeys: 0x801B (Esc -> cancel) 257 | If (H = This.HWND) { 258 | If (W = 0x801B) { ; Esc 259 | This.Cancelled := True 260 | PostMessage, 0x10B3, 0, 0, , % "ahk_id " . H 261 | } 262 | Else { 263 | This.Next := True 264 | SendMessage, 0x10B3, 0, 0, , % "ahk_id " . H 265 | This.Next := True 266 | This.NextSubItem(W) 267 | } 268 | Return False 269 | } 270 | } 271 | ; ------------------------------------------------------------------------------------------------------------------- 272 | ; WM_NOTIFY message handler 273 | ; ------------------------------------------------------------------------------------------------------------------- 274 | On_WM_NOTIFY(W, L) { 275 | Critical, % This.Critical 276 | If (H := NumGet(L + 0, 0, "UPtr") = This.HWND) { 277 | M := NumGet(L + (A_PtrSize * 2), 0, "Int") 278 | ; BeginLabelEdit ------------------------------------------------------------------------------------------------- 279 | If (M = -175) || (M = -105) ; LVN_BEGINLABELEDITW || LVN_BEGINLABELEDITA 280 | Return This.LVN_BEGINLABELEDIT(L) 281 | ; EndLabelEdit --------------------------------------------------------------------------------------------------- 282 | If (M = -176) || (M = -106) ; LVN_ENDLABELEDITW || LVN_ENDLABELEDITA 283 | Return This.LVN_ENDLABELEDIT(L) 284 | ; Double click --------------------------------------------------------------------------------------------------- 285 | If (M = -3) ; NM_DBLCLICK 286 | This.NM_DBLCLICK(L) 287 | } 288 | } 289 | ; ------------------------------------------------------------------------------------------------------------------- 290 | ; LVN_BEGINLABELEDIT notification 291 | ; ------------------------------------------------------------------------------------------------------------------- 292 | LVN_BEGINLABELEDIT(L) { 293 | Static Indent := 4 ; indent of the Edit control, 4 seems to be reasonable for XP, Vista, and 7 294 | If (This.Item = -1) || (This.SubItem = -1) 295 | Return True 296 | H := This.HWND 297 | SendMessage, 0x1018, 0, 0, , % "ahk_id " . H ; LVM_GETEDITCONTROL 298 | This.HEDIT := ErrorLevel 299 | , VarSetCapacity(ItemText, 2048, 0) ; text buffer 300 | , VarSetCapacity(LVITEM, 40 + (A_PtrSize * 5), 0) ; LVITEM structure 301 | , NumPut(This.Item, LVITEM, 4, "Int") 302 | , NumPut(This.SubItem, LVITEM, 8, "Int") 303 | , NumPut(&ItemText, LVITEM, 16 + A_PtrSize, "Ptr") ; pszText in LVITEM 304 | , NumPut(1024 + 1, LVITEM, 16 + (A_PtrSize * 2), "Int") ; cchTextMax in LVITEM 305 | SendMessage, % (A_IsUnicode ? 0x1073 : 0x102D), % This.Item, % &LVITEM, , % "ahk_id " . H ; LVM_GETITEMTEXT 306 | This.ItemText := StrGet(&ItemText, ErrorLevel) 307 | ; Call the user function, if any 308 | If (This.EditUserFunc) 309 | This.EditUserFunc.Call("BEGIN", This.HWND, This.HEDIT, This.Item + 1, This.Subitem + 1, This.ItemText) 310 | SendMessage, 0x000C, 0, % &ItemText, , % "ahk_id " . This.HEDIT 311 | If (This.SubItem > 0) && (This.Blank) { 312 | Empty := "" 313 | , NumPut(&Empty, LVITEM, 16 + A_PtrSize, "Ptr") ; pszText in LVITEM 314 | , NumPut(0,LVITEM, 16 + (A_PtrSize * 2), "Int") ; cchTextMax in LVITEM 315 | SendMessage, % (A_IsUnicode ? 0x1074 : 0x102E), % This.Item, % &LVITEM, , % "ahk_id " . H ; LVM_SETITEMTEXT 316 | } 317 | VarSetCapacity(RECT, 16, 0) 318 | , NumPut(This.SubItem, RECT, 4, "Int") 319 | SendMessage, 0x1038, This.Item, &RECT, , % "ahk_id " . H ; LVM_GETSUBITEMRECT 320 | This.EX := NumGet(RECT, 0, "Int") + Indent 321 | , This.EY := NumGet(RECT, 4, "Int") 322 | If (This.OSVersion < 6) 323 | This.EY -= 1 ; subtract 1 for WinXP 324 | If (This.SubItem = 0) { 325 | SendMessage, 0x101D, 0, 0, , % "ahk_id " . H ; LVM_GETCOLUMNWIDTH 326 | This.EW := ErrorLevel 327 | } 328 | Else 329 | This.EW := NumGet(RECT, 8, "Int") - NumGet(RECT, 0, "Int") 330 | This.EH := NumGet(RECT, 12, "Int") - NumGet(RECT, 4, "Int") 331 | ; Check the column alignement 332 | VarSetCapacity(LVCOL, 56, 0) 333 | , NumPut(1, LVCOL, "UInt") ; LVCF_FMT 334 | SendMessage, % (A_IsUnicode ? 0x105F : 0x1019), % This.SubItem, % &LVCOL, , % "ahk_id " . H ; LVM_GETCOLUMN 335 | If (NumGet(LVCOL, 4, "UInt") & 0x0002) { ; LVCFMT_CENTER 336 | SendMessage, % (A_IsUnicode ? 0x1057 : 0x1011), 0, % &ItemText, , % "ahk_id " . This.HWND ; LVM_GETSTRINGWIDTH 337 | EW := ErrorLevel + This.DW 338 | If (EW < This.MinW) 339 | EW := This.MinW 340 | If (EW < This.EW) 341 | This.EX += ((This.EW - EW) // 2) - Indent 342 | } 343 | ; Register WM_COMMAND handler 344 | This.CommandFunc := ObjBindMethod(This, "On_WM_COMMAND") 345 | , OnMessage(0x0111, This.CommandFunc) 346 | ; Register hotkeys 347 | If !(This.Next) 348 | This.RegisterHotkeys() 349 | This.Cancelled := False 350 | This.Next := False 351 | Return False 352 | } 353 | ; ------------------------------------------------------------------------------------------------------------------- 354 | ; LVN_ENDLABELEDIT notification 355 | ; ------------------------------------------------------------------------------------------------------------------- 356 | LVN_ENDLABELEDIT(L) { 357 | H := This.HWND 358 | ; Unregister WM_COMMAND handler 359 | OnMessage(0x0111, This.CommandFunc, 0) 360 | This.CommandFunc := "" 361 | ; Unregister hotkeys 362 | If !(This.Next) 363 | This.RegisterHotkeys(False) 364 | ItemText := This.ItemText 365 | If !(This.Cancelled) 366 | ControlGetText, ItemText, , % "ahk_id " . This.HEDIT 367 | If (ItemText <> This.ItemText) { 368 | If !(This["Changed"]) 369 | This.Changed := [] 370 | This.Changed.Insert({Row: This.Item + 1, Col: This.SubItem + 1, Txt: ItemText}) 371 | } 372 | ; Restore subitem's text if changed or blanked out 373 | If (ItemText <> This.ItemText) || ((This.SubItem > 0) && (This.Blank)) { 374 | VarSetCapacity(LVITEM, 40 + (A_PtrSize * 5), 0) ; LVITEM structure 375 | , NumPut(This.Item, LVITEM, 4, "Int") 376 | , NumPut(This.SubItem, LVITEM, 8, "Int") 377 | , NumPut(&ItemText, LVITEM, 16 + A_PtrSize, "Ptr") ; pszText in LVITEM 378 | SendMessage, % (A_IsUnicode ? 0x1074 : 0x102E), % This.Item, % &LVITEM, , % "ahk_id " . H ; LVM_SETITEMTEXT 379 | } 380 | If !(This.Next) 381 | This.Item := This.SubItem := -1 382 | This.Cancelled := False 383 | This.Next := False 384 | ; Call the user function, if any 385 | If (This.EditUserFunc) 386 | This.EditUserFunc.Call("END", This.HWND, This.HEDIT, This.Item + 1, This.Subitem + 1, ItemText) 387 | Return False 388 | } 389 | ; ------------------------------------------------------------------------------------------------------------------- 390 | ; NM_DBLCLICK notification 391 | ; ------------------------------------------------------------------------------------------------------------------- 392 | NM_DBLCLICK(L) { 393 | H := This.HWND 394 | This.Item := This.SubItem := -1 395 | Item := NumGet(L + (A_PtrSize * 3), 0, "Int") 396 | SubItem := NumGet(L + (A_PtrSize * 3), 4, "Int") 397 | If (This["Columns"]) { 398 | If !This["Columns", SubItem] 399 | Return False 400 | } 401 | If (Item >= 0) && (SubItem >= 0) { 402 | This.Item := Item, This.SubItem := SubItem 403 | If !(This.Next) { 404 | ControlGet, V, List, Count, , % "ahk_id " . H 405 | This.RowCount := V - 1 406 | ControlGet, V, List, Count Col, , % "ahk_id " . H 407 | This.ColCount := V - 1 408 | , NumPut(VarSetCapacity(WINDOWINFO, 60, 0), WINDOWINFO) 409 | , DllCall("GetWindowInfo", "Ptr", H, "Ptr", &WINDOWINFO) 410 | , This.DX := NumGet(WINDOWINFO, 20, "Int") - NumGet(WINDOWINFO, 4, "Int") 411 | , This.DY := NumGet(WINDOWINFO, 24, "Int") - NumGet(WINDOWINFO, 8, "Int") 412 | , Styles := NumGet(WINDOWINFO, 36, "UInt") 413 | SendMessage, % (A_IsUnicode ? 0x1057 : 0x1011), 0, % "WWW", , % "ahk_id " . H ; LVM_GETSTRINGWIDTH 414 | This.MinW := ErrorLevel 415 | SendMessage, % (A_IsUnicode ? 0x1057 : 0x1011), 0, % "III", , % "ahk_id " . H ; LVM_GETSTRINGWIDTH 416 | This.DW := ErrorLevel 417 | , SBW := 0 418 | If (Styles & 0x200000) ; WS_VSCROLL 419 | SysGet, SBW, 2 420 | ControlGetPos, LX, LY, LW, , , % "ahk_id " . H 421 | This.LX := LX 422 | , This.LY := LY 423 | , This.LR := LX + LW - (This.DX * 2) - SBW 424 | , This.LW := LW 425 | , This.SW := SBW 426 | , VarSetCapacity(RECT, 16, 0) 427 | , NumPut(SubItem, RECT, 4, "Int") 428 | SendMessage, 0x1038, %Item%, % &RECT, , % "ahk_id " . H ; LVM_GETSUBITEMRECT 429 | X := NumGet(RECT, 0, "Int") 430 | If (SubItem = 0) { 431 | SendMessage, 0x101D, 0, 0, , % "ahk_id " . H ; LVM_GETCOLUMNWIDTH 432 | W := ErrorLevel 433 | } 434 | Else 435 | W := NumGet(RECT, 8, "Int") - NumGet(RECT, 0, "Int") 436 | R := LW - (This.DX * 2) - SBW 437 | If (X < 0) 438 | SendMessage, 0x1014, % X, 0, , % "ahk_id " . H ; LVM_SCROLL 439 | Else If ((X + W) > R) 440 | SendMessage, 0x1014, % (X + W - R + This.DX), 0, , % "ahk_id " . H ; LVM_SCROLL 441 | } 442 | PostMessage, % (A_IsUnicode ? 0x1076 : 0x1017), %Item%, 0, , % "ahk_id " . H ; LVM_EDITLABEL 443 | } 444 | Return False 445 | } 446 | ; ------------------------------------------------------------------------------------------------------------------- 447 | ; Next subItem 448 | ; ------------------------------------------------------------------------------------------------------------------- 449 | NextSubItem(K) { 450 | ; Hotkeys: 0x8009 (Tab -> right), 0x8409 (Shift+Tab -> left), 0x8028 (Down -> down), 0x8026 (Up -> up) 451 | ; Find the next subitem 452 | H := This.HWND 453 | Item := This.Item 454 | SubItem := This.SubItem 455 | If (K = 0x8009) ; right 456 | SubItem++ 457 | Else If (K = 0x8409) { ; left 458 | SubItem-- 459 | If (SubItem = 0) && This.Skip0 460 | SubItem-- 461 | } 462 | Else If (K = 0x8028) ; down 463 | Item++ 464 | Else If (K = 0x8026) ; up 465 | Item-- 466 | IF (K = 0x8409) || (K = 0x8009) { ; left || right 467 | If (This["Columns"]) { 468 | If (SubItem < This.Columns.MinIndex()) 469 | SubItem := This.Columns.MaxIndex(), Item-- 470 | Else If (SubItem > This.Columns.MaxIndex()) 471 | SubItem := This.Columns.MinIndex(), Item++ 472 | Else { 473 | While (This.Columns[SubItem] = "") { 474 | If (K = 0x8009) ; right 475 | SubItem++ 476 | Else 477 | SubItem-- 478 | } 479 | } 480 | } 481 | } 482 | If (SubItem > This.ColCount) 483 | Item++, SubItem := This.Skip0 ? 1 : 0 484 | Else If (SubItem < 0) 485 | SubItem := This.ColCount, Item-- 486 | If (Item > This.RowCount) 487 | Item := 0 488 | Else If (Item < 0) 489 | Item := This.RowCount 490 | If (Item <> This.Item) 491 | SendMessage, 0x1013, % Item, False, , % "ahk_id " . H ; LVM_ENSUREVISIBLE 492 | VarSetCapacity(RECT, 16, 0), NumPut(SubItem, RECT, 4, "Int") 493 | SendMessage, 0x1038, % Item, % &RECT, , % "ahk_id " . H ; LVM_GETSUBITEMRECT 494 | X := NumGet(RECT, 0, "Int"), Y := NumGet(RECT, 4, "Int") 495 | If (SubItem = 0) { 496 | SendMessage, 0x101D, 0, 0, , % "ahk_id " . H ; LVM_GETCOLUMNWIDTH 497 | W := ErrorLevel 498 | } 499 | Else 500 | W := NumGet(RECT, 8, "Int") - NumGet(RECT, 0, "Int") 501 | R := This.LW - (This.DX * 2) - This.SW, S := 0 502 | If (X < 0) 503 | S := X 504 | Else If ((X + W) > R) 505 | S := X + W - R + This.DX 506 | If (S) 507 | SendMessage, 0x1014, % S, 0, , % "ahk_id " . H ; LVM_SCROLL 508 | Point := (X - S + (This.DX * 2)) + ((Y + (This.DY * 2)) << 16) 509 | SendMessage, 0x0201, 0, % Point, , % "ahk_id " . H ; WM_LBUTTONDOWN 510 | SendMessage, 0x0202, 0, % Point, , % "ahk_id " . H ; WM_LBUTTONUP 511 | SendMessage, 0x0203, 0, % Point, , % "ahk_id " . H ; WM_LBUTTONDBLCLK 512 | SendMessage, 0x0202, 0, % Point, , % "ahk_id " . H ; WM_LBUTTONUP 513 | } 514 | ; ------------------------------------------------------------------------------------------------------------------- 515 | ; Register/UnRegister hotkeys 516 | ; ------------------------------------------------------------------------------------------------------------------- 517 | RegisterHotkeys(Register = True) { 518 | ; WM_HOTKEY := 0x0312, MOD_SHIFT := 0x0004 519 | ; Hotkeys: 0x801B (Esc -> cancel, 0x8009 (Tab -> right), 0x8409 (Shift+Tab -> left) 520 | ; 0x8028 (Down -> down), 0x8026 (Up -> up) 521 | H := This.HWND 522 | If (Register) { ; Register 523 | DllCall("RegisterHotKey", "Ptr", H, "Int", 0x801B, "UInt", 0, "UInt", 0x1B) 524 | , DllCall("RegisterHotKey", "Ptr", H, "Int", 0x8009, "UInt", 0, "UInt", 0x09) 525 | , DllCall("RegisterHotKey", "Ptr", H, "Int", 0x8409, "UInt", 4, "UInt", 0x09) 526 | , DllCall("RegisterHotKey", "Ptr", H, "Int", 0x8028, "UInt", 0, "UInt", 0x28) 527 | , DllCall("RegisterHotKey", "Ptr", H, "Int", 0x8026, "UInt", 0, "UInt", 0x26) 528 | , This.HotkeyFunc := ObjBindMethod(This, "On_WM_HOTKEY") 529 | , OnMessage(0x0312, This.HotkeyFunc) ; WM_HOTKEY 530 | } 531 | Else { ; Unregister 532 | DllCall("UnregisterHotKey", "Ptr", H, "Int", 0x801B) 533 | , DllCall("UnregisterHotKey", "Ptr", H, "Int", 0x8009) 534 | , DllCall("UnregisterHotKey", "Ptr", H, "Int", 0x8409) 535 | , DllCall("UnregisterHotKey", "Ptr", H, "Int", 0x8028) 536 | , DllCall("UnregisterHotKey", "Ptr", H, "Int", 0x8026) 537 | , OnMessage(0x0312, This.HotkeyFunc, 0) ; WM_HOTKEY 538 | , This.HotkeyFunc := "" 539 | } 540 | } 541 | } -------------------------------------------------------------------------------- /Sources/Sample.ahk: -------------------------------------------------------------------------------- 1 | #NoEnv 2 | #Include Class_LV_InCellEdit.ahk 3 | SetBatchLines, -1 4 | If (SubStr(A_AhkVersion, 1, 6) < "1.1.20") { 5 | MsgBox, 16, ERROR, Sorry, this script requires AHK 1.1.20+! 6 | ExitApp 7 | } 8 | ; ---------------------------------------------------------------------------------------------------------------------- 9 | LV := " 10 | ( 11 | 1234567890|www.google.com|Row 1 12 | 2345678901|msdn.microsoft.com|Row 2 13 | 3456789012|www.autohotkey.com|Row 3 14 | 4567890123|de.autohotkey.com|Row 4 15 | )" 16 | ; ---------------------------------------------------------------------------------------------------------------------- 17 | LVW := 610 18 | Gui, Margin, 20, 20 19 | Gui, Font, s10, Verdana 20 | Gui, Add, CheckBox, h20 vCL gCommonListView Checked 21 | , % " In-cell editing for common ListView (restricted to columns 1 and 3)" 22 | Gui, Add, ListView, -Readonly y+10 w%LVW% Grid r4 gSubLV1 hwndHLV1 AltSubmit vLV1 23 | , Column 1|Column 2|Column 3|Column 4|Column 5 24 | Loop, Parse, LV, `n 25 | If (A_LoopField) { 26 | StringSplit, F, A_LoopField, | 27 | LV_Add("", F1, F2, F3, A_Index, A_Index) 28 | } 29 | Loop, 3 30 | LV_ModifyCol(A_Index, "200") 31 | ; Create a new instance of LV_InCellEdit for HLV1 32 | ICELV1 := New LV_InCellEdit(HLV1) 33 | ; Restrict editable columns 34 | ICELV1.SetColumns(1, 3) 35 | Gui, Add, CheckBox, h20 vHL gHiddenCol1ListView Checked 36 | , % " In-cell editing for ListView with options HiddenCol1 && BlankSubItem" 37 | Gui, Add, ListView, xm y+10 w%LVW% -Readonly Grid r4 gSubLV2 hwndHLV2 AltSubmit vLV2 38 | , Column 0|Column 1|Column 2|Column 3|Column 4|Column 5 39 | Loop, Parse, LV, `n 40 | If (A_LoopField) { 41 | StringSplit, F, A_LoopField, | 42 | LV_Add("", "", F1, F2, F3, A_Index, A_Index) 43 | } 44 | LV_ModifyCol(1, 0) 45 | Loop, 3 46 | LV_ModifyCol(A_Index + 1, "200") 47 | ; Create a new instance of LV_InCellEdit for HLV2 with options HiddenCol1 and BlankSubItem 48 | ICELV2 := New LV_InCellEdit(HLV2, True, True) 49 | Gui, Show, , In-Cell ListView Editing with DoubleClick %A_AhkVersion% 50 | Return 51 | ; ---------------------------------------------------------------------------------------------------------------------- 52 | GuiClose: 53 | GuiEscape: 54 | ExitApp 55 | ; ---------------------------------------------------------------------------------------------------------------------- 56 | CommonListView: 57 | GuiControlGet, CL 58 | If (CL) 59 | ICELV1.OnMessage() 60 | Else 61 | ICELV1.OnMessage(False) 62 | Return 63 | ; ---------------------------------------------------------------------------------------------------------------------- 64 | HiddenCol1ListView: 65 | GuiControlGet, HL 66 | If (HL) 67 | ICELV2.OnMessage() 68 | Else 69 | ICELV2.OnMessage(False) 70 | Return 71 | ; ---------------------------------------------------------------------------------------------------------------------- 72 | SubLV1: 73 | ; Check for changes 74 | If (A_GuiEvent == "F") { 75 | If (ICELV1["Changed"]) { 76 | Msg := "" 77 | For I, O In ICELV1.Changed 78 | Msg .= "Row " . O.Row . " - Column " . O.Col . " : " . O.Txt 79 | ToolTip, % "Changes in " . A_GuiControl . "`r`n`r`n" . Msg 80 | SetTimer, KillToolTip, 2000 81 | ICELV1.Remove("Changed") 82 | } 83 | } 84 | Return 85 | ; ---------------------------------------------------------------------------------------------------------------------- 86 | SubLV2: 87 | ; Use key 'e' to edit the first editable cell of the focused row, if any 88 | If (A_GuiEvent == "K") && (Chr(A_EventInfo) = "e") { 89 | Gui, ListView, %A_GuiControl% 90 | If (Row := LV_GetNext(0, "Focused")) 91 | ICELV2.EditCell(Row) 92 | } 93 | Return 94 | ; ---------------------------------------------------------------------------------------------------------------------- 95 | KillToolTip: 96 | ToolTip 97 | Return --------------------------------------------------------------------------------