AutoHotkey Community

It is currently May 27th, 2012, 6:30 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: May 10th, 2010, 2:15 pm 
Offline

Joined: July 6th, 2005, 6:31 am
Posts: 15
Here's the story:

http://www.autohotkey.com/forum/viewtopic.php?t=57511&highlight=virtual+list

And here's the example:

Image

Really fast, isn't it? (even faster than nDroid, I think :oops: )

That's my first try to implement a virtual listview, it seems that it's possible to have a virtual listview in AHK, everything works fine, however the listview keeps flickering while the column is being draged, and I have no idea why that happen and how to solve it.

I might try to fix it the other time (but I'm not sure if I have time :( ).

If anyone have any idea(about how to make the listview flicker-free), please let me know! :wink:

Usage:
1. Hit the button and locate the folder.
2. Type your search patterns into the edit box.
3. That's it.

Note:
1. The search patterns should be space-separated.
2. Each pattern is not case-senstivie.
3. Any pattern prefixed with a minus "-" is considered as exclusion.
4. Any pattern prefixed with a backslash "\" is considered to be matched with (any part of) the folde, otherwise it would be matched with (any part of) the file name.

And yes, this script just do nothing but listing the files, feel free to modify it and of course share your revision :wink:

Code:
;
; AutoHotkey Version: 1.0.48.05
; Language:       English
; Platform:       Win9x/NT
; Author:         Yonken
;
; Script Function:
;   To enumerate the files of specified folder(s), filter out specified type(s), list and
; update the result in real time as the search pattern changed.
;   Use at your own risk.
;

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
SetBatchLines, -1   ; Never sleep

WM_NOTIFY            := 0x004E
LVN_FIRST               := -100

LVN_GETDISPINFOA        := (LVN_FIRST-50)   ; For ANSI version
LVN_GETDISPINFOW        := (LVN_FIRST-77)   ; For unicode version
LVN_GETDISPINFO         := LVN_GETDISPINFOW

LVM_FIRST            := 0x1000
LVM_SETITEMCOUNT      := (LVM_FIRST + 47)
LVM_REDRAWITEMS         := (LVM_FIRST + 21)

LVS_OWNERDATA         := 0x1000

LPSTR_TEXTCALLBACKA      := -1

sizeofNMHDR            := 12
sizeofLVITEM         := 40

LVSICF_NOINVALIDATEALL   := 0x00000001

LVIF_TEXT            := 0x0001
LVIF_IMAGE            := 0x0002
LVIF_STATE            := 0x0008

CP_ACP                := 0           ; default to ANSI code page

pRtlFillMemory         := GetProcAddressInModule("RtlFillMemory")
pWideCharToMultiByte   := GetProcAddressInModule("WideCharToMultiByte")
pMultiByteToWideChar   := GetProcAddressInModule("MultiByteToWideChar")
pStrCpy               := GetProcAddressInModule("strcpy", "msvcrt")

g_strAppName         := "Smart Open File"
g_strVersion         := "2010.5.10"
g_strTitle            := g_strAppName A_Space g_strVersion
g_hMainWnd            := 0

g_nFilesCount         := 0
g_nMatchCount         := 0
g_MatchIndices         = -1


; Allow the user to maximize or drag-resize the window:
Gui +Resize

; Create some buttons:
Gui, Add, Button, Default vBtnLoadFolder gButtonLoadFolder, &Load a folder

; Create the ListView with two columns, Name and Size:
Gui, Add, ListView, Grid xm r20 w700 vMyListView Hwndg_hMyListView +%LVS_OWNERDATA%, Name|In Folder|Modified|Size (KB)|Type

; Create an ImageList so that the ListView can display some icons:
ImageListID1 := IL_Create(10)
ImageListID2 := IL_Create(10, 10, true)  ; A list of large icons to go with the small ones.

; Attach the ImageLists to the ListView so that it can later display the icons:
LV_SetImageList(ImageListID1)
LV_SetImageList(ImageListID2)

ListView_SetItemCount(g_hMyListView, g_nMatchCount)

Gui, Add, Edit, vEditSearchString gOnChangeSearchString,

Gui, Show, , %g_strTitle% [0 of 0]

OnMessage(WM_NOTIFY, "OnNotify")

Gui, +LastFound
WinSet, ReDraw      ; Invalidate the list-view

g_hMainWnd := WinExist()

return

GuiEscape:
   GuiClose:  ; Indicate that the script should exit automatically when the window is closed.
   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 - 70)
   GuiControl, Move, EditSearchString, % "W" . (A_GuiWidth - 20) . " Y" . (A_GuiHeight - 30)
return

ButtonLoadFolder:
   Gui +OwnDialogs  ; Forces user to dismiss the following dialog before using main window.
   FileSelectFolder, Folder,, 3, Select a folder to read:
   if not Folder  ; The user canceled the dialog.
      return
      
   g_nMatchCount := 0
   WinSetTitle, , , %g_strTitle% [%g_nMatchCount% of %g_nFilesCount%]

   GuiControl, Disable, BtnLoadFolder
   SetTimer, FileLoadProgressTimer, 100
   
   g_arrResult := ; Free the memory
   g_nFilesCount := LoadFolder(Folder, "g_arrResult")
   
   Gosub, OnChangeSearchString
   
   Gui +LastFound
   GuiControl, Enable, BtnLoadFolder
   SetTimer, FileLoadProgressTimer, Off
return

OnChangeSearchString:
   GuiControl, -Redraw, MyListView  ; Improve performance by disabling redrawing during load.
   nMatchCount := g_nFilesCount
   bNeedSetFocus := false
   If (g_nFilesCount > 0)
   {
      GuiControlGet, strSearchPattern, , EditSearchString
      
      strSearchPattern := RegExReplace(strSearchPattern, "S)[\s]+", "|")   ; Replace multiple whitespaces with a single character
      if(strSearchPattern = "" || strSearchPattern = "|")
      {
         ; Nothing is entered, select the first one
         g_MatchIndices = -1
         bNeedSetFocus := true
      }
      Else
      {
         If ( SubStr(strSearchPattern, StrLen(strSearchPattern)) == "|")
            strSearchPattern := SubStr(strSearchPattern, 1, StrLen(strSearchPattern)-1)
         StringSplit, arrSearchPatterns, strSearchPattern, |
         
         g_MatchIndices =
         VarSetCapacity(g_MatchIndices, g_nFilesCount * 4)
         
         nMatchCount := 0

         Loop, %g_nFilesCount%
         {
            strName := g_arrResult%A_Index%_NameAnsi
            strFolder := g_arrResult%A_Index%_FolderAnsi
            bMatch := true
            Loop %arrSearchPatterns0%
            {
               if ( !IsMatch(strName, strFolder, arrSearchPatterns%A_Index%) )
               {
                  bMatch := false
                  Break
               }
            }
            if (bMatch)
            {
               NumPut(A_Index, g_MatchIndices, nMatchCount * 4)
               ++nMatchCount
            }
         }
      }
   }
   Else
   {
      g_MatchIndices = -1
   }
   g_nMatchCount := nMatchCount
   ListView_SetItemCount(g_hMyListView, g_nMatchCount)
   ListView_RedrawItems(g_hMyListView, 0, -1)
   ;ToolTip, Done searching %strSearchPattern%
   
   LV_ModifyCol()  ; Auto-size each column to fit its contents.
   GuiControl, +Redraw, MyListView  ; Re-enable redrawing (it was disabled above).
   
   If (bNeedSetFocus)
   {
      GuiControl, Focus, SysListView321,
      Send, {Home}
      GuiControl, Focus, EditSearchString,
   }

   WinSetTitle, , , %g_strTitle% [%g_nMatchCount% of %g_nFilesCount%]
Return

IsMatch(ByRef strFileName, ByRef strFolder, strSubPattern)
{
   chChar1         := SubStr(strSubPattern, 1, 1)
   chChar2         := SubStr(strSubPattern, 2, 1)
   bIsExclude      := chChar1 == "-" || chChar2 == "-"
   bIsInFolder      := chChar1 == "\" || chChar2 == "\"
   
   strSubPattern := RegExReplace(strSubPattern, "S)^[-\\]+")
   If (strSubPattern = "")
      return true
   /*
   StringReplace, strSubPattern, strSubPattern, -
   StringReplace, strSubPattern, strSubPattern, \
   */
   strSearchText   := bIsInFolder ? strFolder : strFileName
   bMatch         := InStr(strSearchText, strSubPattern)
   if (bIsExclude)
      bMatch := !bMatch

   return bMatch
}

FileLoadProgressTimer:
   Gui +LastFound
   WinSetTitle, , , %g_strTitle% [%g_nMatchCount% of %g_nFilesCount%]
Return

/*
typedef struct tagNMHDR
{
    HWND  hwndFrom;
    UINT  idFrom;
    UINT  code;         // NM_ code
}   NMHDR;

 NMHDR *pnm
*/
OnNotify(idCtrl, pnmh)
{
   global g_hMyListView, LVN_GETDISPINFO
   hwndFrom := DecodeInteger( "uint4", pnmh, 0 )
   if ( hwndFrom == g_hMyListView )
   {
      idFrom := DecodeInteger( "uint4", pnmh, 4)
      code := DecodeInteger( "uint4", pnmh, 8)
      nCode := code > 0x7FFFFFFF ? -(~code) - 1 : code
      if ( nCode == LVN_GETDISPINFO )
      {
         OnGetDispInfo(pnmh)
      }
    }
}

/*
NMLVDISPINFO* pnmv

typedef struct tagNMLVDISPINFO {
    NMHDR hdr;
    LVITEM item;
} NMLVDISPINFO;

typedef struct _LVITEM {
    UINT mask;     0
    int iItem;     4
    int iSubItem;  8
    UINT state;    12
    UINT stateMask; 16
    LPTSTR pszText; 20
    int cchTextMax; 24
    int iImage;     28
    LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
    int iIndent;
#endif
#if (_WIN32_WINNT >= 0x560)
    int iGroupId;
    UINT cColumns; // tile view columns
    PUINT puColumns;
#endif
#if (_WIN32_WINNT >= 0x0600)
    int* piColFmt;
    int iGroup;
#endif
} LVITEM, *LPLVITEM;

*/
OnGetDispInfo(pnmv)
{
   global
   iItemOffset      := sizeofNMHDR + 4
   iItem         := DecodeInteger( "uint4", pnmv, iItemOffset)
   
   if (iItem < 0 || iItem > g_nMatchCount)
      return   ; requesting invalid item
   
   maskOffset      := sizeofNMHDR + 0
   mask         := DecodeInteger( "uint4", pnmv, maskOffset)
   
   if (mask & LVIF_TEXT)
   {
      iSubItemOffset   := sizeofNMHDR + 8
      iSubItem      := DecodeInteger( "uint4", pnmv, iSubItemOffset)
      
      pszTextOffset   := sizeofNMHDR + 20
      pszText         := DecodeInteger( "uint4", pnmv, pszTextOffset)
      
      If (g_MatchIndices = -1)
         nIndex := iItem + 1
      else
         nIndex := NumGet(g_MatchIndices, iItem * 4)
         
      ;ToolTip, Row %iItem% Index in list %nIndex%
      
      pstrText := 0
      if (0 == iSubItem)
         pstrText := &g_arrResult%nIndex%_Name
      Else if (1 == iSubItem)
         pstrText := &g_arrResult%nIndex%_Folder
      Else if (2 == iSubItem)
         pstrText := &g_arrResult%nIndex%_Modified
      Else if (3 == iSubItem)
         pstrText := &g_arrResult%nIndex%_SizeKB
      Else if (4 == iSubItem)
         pstrText := &g_arrResult%nIndex%_Ext
      EncodeInteger( pstrText, 4, pnmv, pszTextOffset )
   }
   if (mask & LVIF_STATE)
   {
      stateOffset := sizeofNMHDR + 12
      EncodeInteger( 0, 4, pnmv, stateOffset )
   }
   if (mask & LVIF_IMAGE)
   {
      iImageOffset := sizeofNMHDR + 28
      EncodeInteger( -1, 4, pnmv, iImageOffset )
   }
}

LoadFolder(ByRef strFolder, arrResultName, ByRef strExtInclude = "", ByRef strExtExclude = "", bRecursive = true)
{
   global   ; This is important for creating/accessing array
   nTotalFiles := 0
   nIndex := g_nFilesCount+1
   
   ; Check if the last character of the folder name is a backslash, which happens for root
   ; directories such as C:\. If it is, remove it to prevent a double-backslash later on.
   StringRight, LastChar, strFolder, 1
   if LastChar = \
      StringTrimRight, strFolder, strFolder, 1  ; Remove the trailing backslash.

   ; Ensure the variable has enough capacity to hold the longest file path. This is done
   ; because ExtractAssociatedIconA() needs to be able to store a new filename in it.
   VarSetCapacity(Filename, 260)
   sfi_size = 352
   VarSetCapacity(sfi, sfi_size)

   ; Gather a list of file names from the folder
   Loop %strFolder%\*.*, 0, %bRecursive%
   {
      ;MsgBox %A_LoopFileName%
      
      ; filter out
      If (strExtExclude != "")
      {
         if A_LoopFileExt in %strExtExclude%
         {
            Continue
         }
      }
      If (strExtInclude != "")
      {
         if A_LoopFileExt not in %strExtInclude%
         {
            Continue
         }
      }
      
      ; Name|In Folder|Modified|Size (KB)|Type
      FormatTime, FileTimeModified, %A_LoopFileTimeModified% LSys R D1   ;, MM/dd/yyyy HH:mm
      
      ToWideChar(A_LoopFileName, %arrResultName%%nIndex%_Name)
      ToWideChar(A_LoopFileExt, %arrResultName%%nIndex%_Ext)
      ToWideChar(A_LoopFileDir, %arrResultName%%nIndex%_Folder)
      ToWideChar(FileTimeModified, %arrResultName%%nIndex%_Modified)
      ToWideChar(A_LoopFileSizeKB, %arrResultName%%nIndex%_SizeKB)
      
      ; We need this for pattern matching
      %arrResultName%%nIndex%_NameAnsi := A_LoopFileName
      %arrResultName%%nIndex%_FolderAnsi := A_LoopFileDir
      
      ++nIndex
      ++nTotalFiles
      ++g_nFilesCount
   }
   return nTotalFiles
}

#IfWinActive ahk_class AutoHotkeyGUI
~Up::
~+Up::
   IfWinNotActive, ahk_id %g_hMainWnd%,
   {
      Send, {Up}
      Return
   }
   
   GuiControl, Focus, SysListView321,
   ;ControlSend, SysListView321, {Up}, %g_hMainWnd%
Return

~Down::
~+Down::
   IfWinNotActive, ahk_id %g_hMainWnd%,
   {
      Send, {Down}
      Return
   }
   
   GuiControl, Focus, SysListView321,
   ;ControlSend, SysListView321, {Down}, %g_hMainWnd%
Return
#IfWinActive

;;;;;;;;;;;;;;;;;;; Helper Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ListView_RedrawItems(hwndLV, iFirst, iLast)
{
   global LVM_REDRAWITEMS
   SendMessage, LVM_REDRAWITEMS, iFirst, iLast, , ahk_id %hwndLV%
   return %ErrorLevel%
}

ListView_SetItemCount(hwndLV, cItems)
{
   global LVM_SETITEMCOUNT, LVSICF_NOINVALIDATEALL
    SendMessage, LVM_SETITEMCOUNT, cItems, LVSICF_NOINVALIDATEALL, , ahk_id %hwndLV%
   return %ErrorLevel%
}

TransformWideCharToMultiByte(pWideChar, nWideCharNumber)
{
   Global CP_ACP
   nRequiredSize := WideCharToMultiByte(CP_ACP, 0, pWideChar, nWideCharNumber, 0, 0, 0, 0)
   if(nRequiredSize > 0)
   {
      VarSetCapacity( pMultiByteBuffer, nRequiredSize, 0 )
      nBytesWritten := WideCharToMultiByte(CP_ACP, 0, pWideChar, nWideCharNumber, &pMultiByteBuffer, nRequiredSize, 0, 0)
      return pMultiByteBuffer
   }
   return 0
}

TransformMultiByteToWideChar(pMultiByte, ByRef pWideCharBuffer)
{
   Global CP_ACP
   nRequiredSize := MultiByteToWideChar(CP_ACP, 0, pMultiByte, -1, 0, 0)
   if(nRequiredSize > 0)
   {
      VarSetCapacity( pWideCharBuffer, nRequiredSize<<1, 0 )
      nBytesWritten := MultiByteToWideChar(CP_ACP, 0, pMultiByte, -1, &pWideCharBuffer, nRequiredSize)
      return nBytesWritten
   }
   return -1
}

ToWideChar(strAnsiText, ByRef strUnicodeText)
{
   nStrLen := StrLen(strAnsiText)
   VarSetCapacity(strUnicodeText, (nStrLen+1)*2, 0)
   TransformMultiByteToWideChar(&strAnsiText, strUnicodeText)
   Return strUnicodeText
}

WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar)
{
   global pWideCharToMultiByte
   return DllCall(pWideCharToMultiByte, "UInt", CodePage, "UInt", dwFlags, "UInt", lpWideCharStr, "Int", cchWideChar, "UInt", lpMultiByteStr, "Int", cbMultiByte, "UInt", lpDefaultChar, "UInt", lpUsedDefaultChar)
}

MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar)
{
   global pMultiByteToWideChar
   return DllCall(pMultiByteToWideChar, "UInt", CodePage, "UInt", dwFlags, "UInt", lpMultiByteStr, "Int", cbMultiByte, "UInt", lpWideCharStr, "Int", cchWideChar)
}

DecodeInteger( p_type, p_address, p_offset, p_hex=true )
{
  old_FormatInteger := A_FormatInteger
  ifEqual, p_hex, 1, SetFormat, Integer, hex
  else, SetFormat, Integer, dec
  StringRight, size, p_type, 1
  loop, %size%
      value += *( ( p_address+p_offset )+( A_Index-1 ) ) << ( 8*( A_Index-1 ) )
  if ( size <= 4 and InStr( p_type, "u" ) != 1 and *( p_address+p_offset+( size-1 ) ) & 0x80 )
      value := -( ( ~value+1 ) & ( ( 2**( 8*size ) )-1 ) )
  SetFormat, Integer, %old_FormatInteger%
  return, value
}

EncodeInteger( p_value, p_size, p_address, p_offset )
{
   global pRtlFillMemory
   loop, %p_size%
      DllCall( pRtlFillMemory, "uint", p_address+p_offset+A_Index-1, "uint", 1, "uchar", p_value >> ( 8*( A_Index-1 ) ) )
}

GetProcAddressInModule(strFuncName, strModuleName = "kernel32")
{
   return DllCall("GetProcAddress", uint, DllCall("GetModuleHandle", str, strModuleName), str, strFuncName)
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 10th, 2010, 6:57 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Resizing column makes script freeze here.
Items are not visible in the list until scrolling.

It would be interesting to see how this script behaves when ported to AHK_L. When I did so for my problematic ListViews problems did disappear (or minimize or change) due to the "fix" Lexikos included in recent release.

I thought subclassing was an issue with listview, but since you don't use it, I guess WM_NOTIFY is problematic.

BTW, nobody, to my knowledge, solved this issue so far. Try AHK_L and tell the observations. Thats probably better solution anyway, as of Unicode so you will simplify code by removing unicode conversion dllcalls.

Anyway, perhaps the problem could be solved using something else then ListView.

On related note, I would rly like to see module/function that makes ordinary list view "virtuaL"

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 11th, 2010, 3:34 am 
Offline

Joined: July 6th, 2005, 6:31 am
Posts: 15
majkinetor wrote:
Resizing column makes script freeze here.
Items are not visible in the list until scrolling.

It would be interesting to see how this script behaves when ported to AHK_L. When I did so for my problematic ListViews problems did disappear (or minimize or change) due to the "fix" Lexikos included in recent release.


When this script ported to AutoHotkey_La (ANSI version), the flickering issue is gone for good, but it failed to display anthing in AutoHotkey_Lw (Unicode version), any idea?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 16th, 2010, 2:30 pm 
Offline

Joined: August 15th, 2009, 7:20 am
Posts: 308
This is very interesting. I wish I knew how to adapt it. I'd prefer the search bar at the top and a fixed location rather than using a button. Then there's the problem of how to get a click to open a file. But I do like the speed.

GP


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 13th, 2011, 2:27 am 
Offline

Joined: April 23rd, 2009, 6:05 pm
Posts: 34
Hey Yonken,

I was wondering if you got anywhere with getting this script to work in AutoHotKey_L (Uni). I was hoping to use this in a personal project but it doesn't work as you mentioned.

Bob


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Apollo, rbrtryn and 25 guests


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