(Click Here for the Current Issue)

Your Online Computer and Internet Resource
Weekly, Fridays!
Don't miss an issue! Free Weekly Subscriptions! (Click Here!)

ComputorEdge Site Map and Site Search!
(Click here!)

Download ComputorEdge as an E-book
EPUB Format (iPad, Nook)
(Click Here)
MOBI Format (Kindle)
(Click Here)
"How to Read E-Books" Visit ComputorEdge E-Books!

Click to change regions!

Previous Week  | Change Regions  | Sponsor Information  | San Diego Advertisers  | Archive  | Site Search  | Contact Info |  Next Week
Past Issue Date: 08/02/13
Theme: "Driverless and Flying Cars, Science Funding, and More!"
(Click Here for the Table of Contents)

Prompted by a question, Jack makes improvements to his latest AutoHotkey To-Do List app. The columns read easier, the window resizes, and the app saves the window's last configuration, plus more.

    By Jack Dunning    
††† Close Nav Menu (Printer Friendly Version)      (E-mail to a Friend)      (Add to Favorites)     (Link to this Article)     (Free e-mail subscription!)

Tell us what you think
About This Article!
View Columns
Last Six Months

Improvements on the To-Do List App
“Tips and Tricks for Making AutoHotkey Windows Easier to Resize, Position and Use”
by Jack Dunning

As I explore the power of the AutoHotkey ListView Graphic User Interface (GUI), which is similar in operation and function to the Windows Explorer file list window, I keep getting more ideas for possible applications. I'm already planning a functional address book of my favorite people that works with multiple programs capable of adding addresses to documents, sending e-mails, calculating ages, etc. I also have a concept for a barebones calorie counter and an AutoHotkey app control panel. In addition to the techniques applied in this column to the To-Do List app, there is plenty to learn in the next few weeks. (I may even integrate some subroutines from the Reminder script to make the To-Do List app yell at me at a designated time using the Windows voice. But that might be nagging.)

Unless readers have been following along from the beginning or they are already familiar with writing scripts of any type, I don't expect that everyone will understand what I address here. However, if you are motivated to get the most out of your Windows computer,
ComputorEdge has some beginner's AutoHotkey Web pages and I've written a couple of newbie AutoHotkey e-books which start with simple, but immediately useful, AutoHotkey techniques building up your own knowledge base bit by bit.

Last week's column introducing the To-Do List app brought this question from Ron Cerrato:

I noticed that the window is a fixed size and can't be maximized or changed, only minimized or closed. Also, the list can be very long vertically since vertical scrolling is unlimited but horizontal scrolling isn't helpful for viewing a very long item. You can type a long item in but must edit/copy/notepad-paste to see it. Obviously, I'm trying to extend its applicability beyond that of a to-do list.

Ron, I glad to hear the you want to extend the little script "beyond that of a to-do list." That is the entire purpose of all the writing I do about AutoHotkey. The fact that one of the apps I talk about may be directly beneficial to someone is incidental to my goal of helping more people use AutoHotkey to enhance their Windows computing experience. Too many people are ready to accept the standard way of doing things, not realizing how many basic AutoHotkey tricks can simplify their computing life.

You bring up some good questions which are important for adding flexibility to the AutoHotkey GUI windows. (I've encountered some of these same issues in the address book app which I've just started.) The problem is that you don't always know in advance how big or what shape you want the window to be. That makes this an apt time to demonstrate how to add automatic resizing capabilities to your AutoHotkey windows, plus how to control column width in the ListView control.

Controlling ListView Column Widths

The To-Do List app from last week's column doesn't handle the resizing of columns well. Although you can manually resize a column as shown in Figure 1, it's best if the column automatically resizes based upon its content. Otherwise, you might see truncated text as shown.


Figure 1. Columns can be manually resized in ListView by dragging (click, hold, and drag) the almost invisible vertical line in the heading to the left or right. If the text is longer than the column width, the view may be truncated.


If you drag the column resizing bar beyond the edge of the ListView, a horizontal scrollbar will appear. But this is not necessarily intuitive. It would be better to automatically change the column width (and add the horizontal scrollbar based upon the length of the line of text.

For this reason the ListView control includes the LV_ModifyCol() function for making changes to columns. Using the format LV_ModifyCol([ColumnNumber, Options, ColumnTitle]), the column number is specified, starting on the left end with the number 1. Then options are added (all enclosed in one set of quotes and separated by a space or tab) followed by the new column title (also enclosed in quotes), if needed. The basic form of the function:
LV_ModifyCol()
added to the appropriate routines in the script (before the first Gui, Show and after any changes) forces all column widths to conform to the length of the longest item in each column respectively. If the total column widths go beyond the edge of the ListView, then the horizontal scrollbar is added.

This form of the LV_ModifyCol() for automatically resizing column widths is great—unless there is no data in a column. Then, the column width is minimized leaving the column title unreadable as shown in the image at the left. While the title can be expanded by dragging it to the right, doing this becomes tedious—especially if there are numerous columns with no data. Leaving out the LV_ModifyCol() displays the column title properly, but then we're back to our original problem.

The solution is adding the AutoHdr option to the function:
LV_ModifyCol(1,"AutoHdr")
This make the designated column conform to either the title width or the data in the column, whichever is longer (see Figure 2). Note that the scrollbar is automatically added to the ListView when the column goes beyond the right edge.


Figure 2. By adding the "AutoHdr" option to the LV_ModifyCol() function, the designated column takes on the width of the title or data, whichever is wider. Scrollbars are added automatically, if needed.


Making GUIs Resizable

Ron also noted that "that the window is a fixed size and can't be maximized or changed" in the ToDoList.ahk script. It's much easier if people can resize a GUI window to suit themselves. There is a simple way to make a GUI window resizable. Among the other GUI +/-options, such as +AlwaysOnTop, AutoHotkey has a +Resize option which makes any GUI window resizable by dragging (left-click and hold while moving the mouse) any corner or edge of the window. Simply add the Resize option to the Gui +AlwaysOnTop line of code already found in the script:
Gui +AlwaysOnTop +Resize
This makes any GUI window respond to the maximize and restore button, as well as, resizing by dragging an edge or corner. However, there is a problem. Even though the windows can be dragged to a new size and shape, the controls inside remain at the same size gaining us nothing (see Figure 3).


Figure 3. Add resize to the GUI options to add a maximize button at the top right and allow resizing by dragging. But, it only affects the main GUI window.


In order to resize GUI controls as the main GUI window is adjusted, the special GUI window event label (subroutine) GuiSize must be added to the script. As the main GUI is moved or resized, the GuiSize label is triggered. In this case, the ListView, the Add to list button and the NewItem edit field must be adjusted:
GuiSize:  ; Expand or shrink the controls when resizing window.
if A_EventInfo = 1  ; No action when window is minimized.
    return
; Otherwise, the window has been resized or maximized. Resize the ListView to match.
GuiControl, Move, MyListView, % "W" . (A_GuiWidth - 20) . " H" . (A_GuiHeight - 40)
GuiControl, Move, Button1, % "y" . (A_GuiHeight - 30) 
GuiControl, Move, Edit1, % "y" . (A_GuiHeight - 30) . "W" . (A_GuiWidth - 90)
Return
This is a powerful label since it ties changes in the size and position of GUI controls to the resizing of the main GUI window in real-time. It works with the GuiControl, Move command by continually updating the width (w), height (h), and/or positional (x and y) parameters for each control as the window is dragged.

Note that the first line of the GuiSize label uses the built-in A_EventInfo variable to determine if the window has been minimized. When GuiSize is triggered, an A_EventInfo value of 1 means the window is minimized (0 = restored or resized; 2 = maximized).

In the following three lines of code the variables A_GuiHeight and A_GuiWidth (only available within the GuiSize label) are used to calculate the new sizes and locations for each control as the GUI window is resized. Note that only the width and height of the ListView control need adjustment ("W" . (A_GuiWidth - 20) . " H" . (A_GuiHeight - 40)), while the Button and Edit field both need the vertical location adjusted ("y" . (A_GuiHeight - 30)). The Edit expands to the right as the GUI windows get wider ("W" . (A_GuiWidth - 90)). The controls in the app now continuously adjust with the GUI window as it is resized by dragging an edge or corner with the mouse cursor (see Figure 4 and compare to Figure 3 above).


Figure 4. After the GuiSize subroutine is added to the script with the appropriate GuiControl, Move commands for each control, the controls adjust size and position to match the shape of the GUI window.


Save Size and Position on the Screen

After resizing and/or moving the To-Do List windows to suit your needs, it would be nice to have those settings stay the same—even when the app is reloaded. As long as the app is running, the window will maintain its last configuration. Close the window and use either the hotkey combination (+ALT+T) or Show To Do List in the right-click menu from the System Tray icon and the window pops up at its last location with the same size. However, if you exit the app and start up again (or reload), the window returns to its default size and location. The only way to resolve the problem is to save the last set of parameters to a file somewhere.

We could use another file (such as an INI file) to save the data, but since there is already a data file that saves the To-Do List items, it's a natural place to save the window size and position. This can be done by capturing the current position and size of the window each time the data is updated then saving the it to the file. The following lines of code are added to the UpdateFile() function in the ToDoList.ahk script (see last week's column for an explanation of the complete function):
    WinGetPos, X, Y, Width, Height, To Do List
    Width -= 16
    Height -= 38
    FileAppend, x%x% y%y% w%Width% h%Height% `n, ToDoList.txt
The WinGetPos command retrieves the needed position and size parameters from the To Do List window, makes a couple of adjustments, then writes the parameters in the first line of the ToDoList.txt data file (see Figure 5). However, there are a couple of quirks with using this command.


Figure 5. The parameters for the location and size of the To Do List window are saved in the first line of the ToDoList.txt data file.


First, as well as saving the file after any changes, the UpdateFile() function is set to run OnExit. However, if the window has been hidden (closed), then WinGetPos will not detect the window and the settings will be lost. This is resolved by adding DetectHiddenWindows On in the UpdateFile label (subroutine) called by OnExit:
UpdateFile:
  DetectHiddenWindows On
  UpdateFile()
  ExitApp
Return
Now the hidden To Do List window is detected and the size and screen position saved.

The second problem with using the WinGetPos command is that it captures the actual window dimensions while the Gui, Show command for displaying the window later uses only the dimensions for the active area inside the borders of the window. Without adjustments, the window will grow slightly larger (by the dimensions of the borders and title bar) each time the window is reloaded. This is resolved by adjusting the width and height by the frame dimensions in pixels (Width -= 16 and Height -= 38) before saving. (The operation -= subtracts the value from the variable and saves the new value in the same variable. The operation += adds the value to the same variable.)

The FileAppend command is used to add the window dimensions in the format needed by the Gui, Show command to the first line of the previously deleted ToDoList.txt data file. The new UpdateFile() function is as follows:
UpdateFile()
  {
    FileDelete, ToDoList.txt
    WinGetPos, X, Y, Width, Height, To Do List
    Width -= 16
    Height -= 38
    FileAppend, x%x% y%y% w%Width% h%Height% `n, ToDoList.txt
    Loop % LV_GetCount()
     {
       Gui +LastFound
       SendMessage, 4140, A_Index - 1, 0xF000, SysListView321 
       IsChecked := (ErrorLevel >> 12) - 1
       If IsChecked
        {
          LV_GetText(Text, A_Index)
          FileAppend, *%Text% `n, ToDoList.txt
        }
         else
        {
          LV_GetText(Text, A_Index)
          FileAppend, %Text% `n, ToDoList.txt
        }
      }
   }
As can be seen, the Loop that saves the To-Do List items begins immediately after the window's position data is saved. (See last week's column for more details of the UpdateFile() function.)

Now the positions data must be read when the app is first loaded in the auto-execute portion of the script:
IfExist, ToDoList.txt
{
Loop, Read, ToDoList.txt
  {
  If (A_index = 1 and SubStr(A_LoopReadLine, 1, 1) = "x")
     {
       WinPos := A_LoopReadLine
       Continue
     }
  If SubStr(A_LoopReadLine, 1, 1) = "*"
    {
     StringTrimLeft, CheckedText, A_LoopReadLine, 1
     LV_Add("Check", CheckedText)
    }
  Else
     LV_Add("", A_LoopReadLine)
  }
}
Note that a new IF condition has been added to the beginning of this loop in the auto-execute section of the script:
  If (A_index = 1 and SubStr(A_LoopReadLine, 1, 1) = "x")
     {
       WinPos := A_LoopReadLine
       Continue
     }
Since the built-in loop variable A_Index equals 1 for the first line of the file, this line is used for storing position data to the variable WinPos. Just in case an older data file exists without the position data (created by a previous version of ToDoList.ahk) the condition is skipped if the first line does not have the letter "x" in the first position (SubStr(A_LoopReadLine, 1, 1) = "x"). The Continue command causes the loop to increment (skip) to the next value of A_Index and restart the loop without executing any of the subsequent commands in the loop.

Now the saved dimensions (WinPos) must be added to the Gui, Show command:
IfExist, ToDoList.txt
  {
     Gui, Show, %WinPos% , To Do List
  }
Else
  {
     WinGetPos,X1,Y1,W1,H1,Program Manager
     X2 := W1-300
     Gui, Show, x%x2% y50 , To Do List
  }
Unless the ToDoList.txt file is deleted, the default (Else) will only run the first time the script runs.

Directly Editing inside the First Column of ListView

One of the feature of ListView is that the first column (and only the first column) can be directly edited in the column if the default readonly option is turned off:
Gui, Add, ListView, sort r10 checked -readonly vMyListView gMyListView , Items To Do 
The -readonly option is added to the Gui, Add, ListView command allowing editing in the first column (see Figure 6). To edit, select a row in the first column, then click again to edit. Use the ENTER key or deselect to exit editing. ESC to exit editing and revert to original text.


Figure 6. When the -readonly (minus readonly) is added as an option, the first column of the ListView can be directly edited with a second (slow) click of the first column of an item.


The only problem with this form of editing is that the script has not been set up to save this type of edit, although the data would be saved OnExit. To immediately save the edit, the following code is added to the MyListView label called out by the ListView command:
MyListView:
  If A_GuiEvent = e
    UpdateFile()
Return
The built-in variable A_GuiEvent contains the G-Label Notifications (triggers for the MyListView label) for events such as double-click, right-click, column-click, and exit editing. When exit editing (edit field deselected), "e" is stored to A_GuiEvent and MyListView is triggered. In this case the UpdateFile() function runs saving the latest changes to the ToDoList.txt data file.

The Latest Version of ToDoList.ahk

Both the latest AHK (ToDoList.ahk) file and the EXE command file (ToDoList.exe) can be found at the ComputorEdge AutoHotkey Dropbox download site in the ZIP file ToDoList.zip. The complete AutoHotkey code for the To-Do List app follows for easy copy-and-paste into an AHK script:
OnExit, UpdateFile

Gui, default
Gui +AlwaysOnTop +Resize
Gui, Add, ListView, sort r10 checked -readonly vMyListView gMyListView , Items To Do 
Gui, Add, Button, section gAddItem,Add to list
Gui, Add, Edit, ys vNewItem w180 , <Enter New Item Here>

SelectedRow := 0

IfExist, ToDoList.txt
{
Loop, Read, ToDoList.txt
  {
  If (A_index = 1 and SubStr(A_LoopReadLine, 1, 1) = "x")
     {
       WinPos := A_LoopReadLine
       Continue
     }
  If SubStr(A_LoopReadLine, 1, 1) = "*"
    {
     StringTrimLeft, CheckedText, A_LoopReadLine, 1
     LV_Add("Check", CheckedText)
    }
  Else
     LV_Add("", A_LoopReadLine)
  }

}

LV_ModifyCol(1,"AutoHdr")

Menu, MyContextMenu, Add, Edit, EditItem
Menu, MyContextMenu, Add, Delete, DeleteItem
Menu, Tray, Add, Show To Do List, ShowTodo

IfExist, ToDoList.txt
  {
     Gui, Show, %WinPos% , To Do List
  }
Else
  {
     WinGetPos,X1,Y1,W1,H1,Program Manager
     X2 := W1-300
     Gui, Show, x%x2% y50 , To Do List
  }

 
Hotkey, ^!t, ShowTodo
Return

ShowTodo:
Gui, Show,, To Do List
Return

MyListView:
  If A_GuiEvent = e
    UpdateFile()
Return

GuiContextMenu:  ; Launched in response to a right-click or press of the Apps key.
if A_GuiControl <> MyListView  ; Display the menu only for clicks inside the ListView.
    return
  SelectedRow := LV_GetNext()
  LV_GetText(EditText, SelectedRow)
; Show the menu at the provided coordinates, A_GuiX and A_GuiY.  These should be used
; because they provide correct coordinates even if the user pressed the Apps key:
Menu, MyContextMenu, Show , %A_GuiX%, %A_GuiY%
return


DeleteItem:  ; The user selected "Clear" in the context menu.
RowNumber = 0  ; This causes the first iteration to start the search at the top.

Loop
{
    ; Since deleting a row reduces the RowNumber of all other rows beneath it,
    ; subtract 1 so that the search includes the same row number that was previously
    ; found (in case adjacent rows are selected):
    RowNumber := LV_GetNext(RowNumber - 1)
    if not RowNumber  ; The above returned zero, so there are no more selected rows.
        break
    LV_Delete(RowNumber)  ; Clear the row from the ListView.
}
UpdateFile()
return

AddItem:
  Gui, Submit, NoHide

If SelectedRow = 0
{
  LV_Add("", trim(NewItem))
}
else
{
  LV_Modify(SelectedRow,"",Trim(NewItem))
  SelectedRow := 0
  GuiControl, ,Button1, Add to list
}
  UpdateFile()
  LV_ModifyCol(1,"AutoHdr")
Return

EditItem:
  GuiControl, ,Edit1, %EditText%
  GuiControl, ,Button1, Update
Return

UpdateFile:
  DetectHiddenWindows On
  UpdateFile()
  ExitApp
Return

GuiSize:  ; Expand or shrink the ListView in response to the user's resizing of the window.
if A_EventInfo = 1  ; The window has been minimized.  No action needed.
    return
; Otherwise, the window has been resized or maximized. Resize the ListView to match.
GuiControl, Move, MyListView, % "W" . (A_GuiWidth - 20) . " H" . (A_GuiHeight - 40)
GuiControl, Move, Button1, % "y" . (A_GuiHeight - 30) 
GuiControl, Move, Edit1, % "y" . (A_GuiHeight - 30) . "W" . (A_GuiWidth - 90)
Return

UpdateFile()
  {
    FileDelete, ToDoList.txt
    WinGetPos, X, Y, Width, Height, To Do List
    Width -= 16
    Height -= 38
    FileAppend, x%x% y%y% w%Width% h%Height% `n, ToDoList.txt
    Loop % LV_GetCount()
     {
       Gui +LastFound
       SendMessage, 4140, A_Index - 1, 0xF000, SysListView321 
       IsChecked := (ErrorLevel >> 12) - 1
       If IsChecked
        {
          LV_GetText(Text, A_Index)
          FileAppend, *%Text% `n, ToDoList.txt
        }
         else
        {
          LV_GetText(Text, A_Index)
          FileAppend, %Text% `n, ToDoList.txt
        }
      }
   }
*                    *                    *

Now available in e-book format from Amazon, Jack's A Beginner's Guide to AutoHotkey, Absolutely the Best Free Windows Utility Software Ever!: Create Power Tools for Windows XP, Windows Vista, Windows 7 and Windows 8.

Building Power Tools for Windows XP, Windows Vista, Windows 7 and Windows 8, AutoHotkey is the most powerful, flexible, free Windows utility software available. Anyone can instantly add more of the functions that they want in all of their Windows programs, whether installed on their computer or while working on the Web. AutoHotkey has a universality not found in any other Windows utility—free or paid.

Based upon the series of articles in ComputorEdge, Jack takes you through his learning experience as he explores writing simple AutoHotkey scripts for adding repetitive text in any program or on the Web, running programs with special hotkeys or gadgets, manipulating the size and screen location of windows, making any window always-on-top, copying and moving files, and much more. Each chapter builds on the previous chapters.

For an EPUB (iPad, NOOK, etc.) version of A Beginner's Guide to AutoHotkey click here!

*                    *                    *

Jack's latest AutoHotkey book which is comprised of updated, reorganized and indexed columns from the last six months is now available at Amazon for Kindle hardware (or free software) users. Since the columns were not all written in a linear fashion, the book has been reorganized and broken up into parts by topic. The book is not for the complete beginner since it builds on the information in A Beginner's Guide to AutoHotkey. However, if a person is reasonably computer literate, they could go directly to this book for ideas and techniques without the first book.

If you've been following along with my AutoHotkey columns, then there is little new information in the book (although I have added more clarification for techniques I felt were either confusing or wrong). The only reason I can see to buy it would be as a handy reference. The AutoHotkey commands used are included in a special index to the chapters in which they appear. Even I can't remember everything I wrote.

For an EPUB (iPad, NOOK, etc.) version of Digging Deeper into AutoHotkey click here!



Jack is the publisher of ComputorEdge Magazine. He's been with the magazine since first issue on May 16, 1983. Back then, it was called The Byte Buyer. His Web site is www.computoredge.com. He can be reached at . Jack is now in the process of updating and compiling his hundreds of articles and columns into e-books. Currently available:

Just Released!
Hidden Windows Tools for Protecting, Problem Solving and Troubleshooting Windows 8, Windows 7, Windows Vista, and Windows XP Computers.

Jack's
A Beginner's Guide to AutoHotkey, Absolutely the Best Free Windows Utility Software Ever!: Create Power Tools for Windows XP, Windows Vista, Windows 7 and Windows 8 and Digging Deeper Into AutoHotkey.

Our second compilation of stupid
ComputorEdge cartoons from 2011 and 2012 is now available at Amazon! That Does Not Compute, Too! ComputorEdge Cartoons, Volume II: "Do You Like Windows 8 or Would You Prefer an Apple?"

Currently only at Amazon.com,
Jack's Favorite Free Windows Programs: What They Are, What They Do, and How to Get Started!.

Available from Amazon,
Misunderstanding Windows 8: An Introduction, Orientation, and How-to for Windows 8! Also available at Barnes and Noble and ComputorEdge E-Books.


Available exclusively from Amazon,
Windows 7 Secrets Four-in-One E-Book Bundle,
Getting Started with Windows 7: An Introduction, Orientation, and How-to for Using Windows 7,
Sticking with Windows XP—or Not? Why You Should or Why You Should Not Upgrade to Windows 7,
and That Does Not Compute!, brilliantly drawn cartoons by Jim Whiting for really stupid gags by Jack about computers and the people who use them.


††† Close Nav Menu (Printer Friendly Version)      (E-mail to a Friend)      (Add to Favorites)     (Link to this Article)     (Free e-mail subscription!) Share this with the world!

Tell us what you think
About This Article!
View Columns
Last Six Months

Today's Date: 11/28/14
redIT
(Click Banner)

Windows 7 Secrets Essential Bundle
(Click Banner)

Jacks Favorite Free Wiindows Progs
(Click Banner)

Getting Started with Windows 7
(Click Banner)

AutoHotkey, Digging Deeper
(Click Banner)

Misunderstanding Windows 8
(Click Banner)

Contribute to ComputorEdge
(Click Banner)

Get Started OpenOffice/LibreOffice
(Click Banner)

AutoHotkey Applications Book
(Click Banner)

ComputorEdge Cartoons
(Click Banner)

AutoHotkey, The Best Free Utility
(Click Banner)

E-mail Subscriptions San Diego
(Click Banner)

Hidden Windows Tools
(Click Banner)

Digital Daves Product Banner
(Click Banner)

Hi Tech Computer Repair
(Click Banner)

Send mail to with questions about editorial content.
Send mail to with questions or comments about this Web site.
Copyright © 1997-2014 The Byte Buyer, Inc.
ComputorEdge Magazine, P.O. Box 83086, San Diego, CA 92138. (858) 484-1998