Project Search

Share your advanced PureBasic knowledge/code with the community.
jassing
Addict
Addict
Posts: 1885
Joined: Wed Feb 17, 2010 12:00 am

Project Search

Post by jassing »

Here's an old bit of code.. I often found I wanted to search for something, but just w/in a project.
so I slapped this together...

I compiled it, then created a tool "Search Project File"
working dir: %PROJECT
Arguments: "%HOME"
and gave it "ALT-F" short cut.. Now when I need to search all project files, it's relatively easily...

Caveat: Purebasic doesn't write the project file (pbp) to disk until you either close the project or or exit the IDE; so if you add new new files to a project, the project will need to be closed/re-opened before this can search all the files.

Code: Select all

;v3a - Adds menu/options to close search on selecton (dbl click)
;    - Add right-click menu on results to open folder of selection.
;v3  - adds search history (note, if using ADS, history will be lost on rebuild)
;    - adds seaching all code/c-asm/ignore c-asm
;    - fixed regex error 
;    - added "ignore comments" (basic, has issues)
;    - fixed several bugs.
;    - added statusbar to results screen
;    - save search options & history (per project)
;v2a - Adds an icon to windows
;v2  - Simplified: Reduce code, Remove map & structures
;    - fixed stupid programmer code. (aka: very dumb bugs)
;v1b - Windows Only: re-activate window If re-run
;v1a - suggestions by @highend
;    - light changs here&there
;v1  - initial

EnableExplicit

; note, 'ignore comments' and those dealing with c/asm (; and !) 
;       are only considered if they are at the start of the line. 
;       I'll work on this.

;- ---[ PB Form Generated ]---
;{ Search Window

Enumeration FormWindow
  #winSearch
EndEnumeration

Enumeration FormGadget
  #txtSearchFor
  #cboSearchFor
  #chkExact
  #chkCaseSpecific
  #btnSearch
  #chkIgnoreComments
  #optEverywhere
  #optcASM
  #optPBOnly
  #chkIgnoreQuotes
EndEnumeration


Procedure OpenwinSearch(x = 0, y = 0, width = 380, height = 100)
  OpenWindow(#winSearch, x, y, width, height, "Project Search", #PB_Window_SystemMenu | #PB_Window_ScreenCentered|#PB_Window_Invisible)
  HideWindow(#winSearch, 1)
  TextGadget(#txtSearchFor, 10, 10, 70, 15, "Search &For")
  ComboBoxGadget(#cboSearchFor, 80, 5, 170, 25, #PB_ComboBox_Editable)
  CheckBoxGadget(#chkExact, 130, 70, 92, 15, "&Exact Match")
  GadgetToolTip(#chkExact, "Match partial words or not")
  CheckBoxGadget(#chkCaseSpecific, 130, 50, 96, 15, "&Case Specific")
  ButtonGadget(#btnSearch, 260, 5, 100, 25, "&Search")
  DisableGadget(#btnSearch, 1)
  CheckBoxGadget(#chkIgnoreComments, 230, 50, 127, 15, "&Ignore Comments")
  OptionGadget(#optEverywhere, 10, 40, 110, 15, "&All")
  SetGadgetState(#optEverywhere, 1)
  OptionGadget(#optcASM, 10, 60, 110, 15, "c/AS&M only")
  OptionGadget(#optPBOnly, 10, 80, 110, 15, "I&gnore c/ASM")
  CheckBoxGadget(#chkIgnoreQuotes, 230, 70, 127, 15, "Ignore &Quotes")
  GadgetToolTip(#chkIgnoreQuotes, "Will not match text inside quotes")
EndProcedure


;}
;{ Search results window
;
; This code is automatically generated by the FormDesigner.
; Manual modification is possible to adjust existing commands, but anything else will be dropped when the code is compiled.
; Event procedures needs to be put in another source file.
;

Enumeration FormWindow
  #winResults
EndEnumeration

Enumeration FormGadget
  #lstResults
  #chkCloseOnSelect
EndEnumeration


Procedure OpenwinResults(x = 0, y = 0, width = 1050, height = 350)
  OpenWindow(#winResults, x, y, width, height, "Project Search Results", #PB_Window_SystemMenu|#PB_Window_Invisible, WindowID(#winSearch))
  HideWindow(#winResults, 1)
  CreateStatusBar(0, WindowID(#winResults))
  AddStatusBarField(60)
  StatusBarText(0, 0, "Total Files", #PB_StatusBar_Raised)
  AddStatusBarField(50)
  StatusBarText(0, 1, "Label", #PB_StatusBar_Center | #PB_StatusBar_Raised)
  AddStatusBarField(80)
  StatusBarText(0, 2, "Files Matched", #PB_StatusBar_Raised)
  AddStatusBarField(50)
  StatusBarText(0, 3, "Label", #PB_StatusBar_Center | #PB_StatusBar_Raised)
  AddStatusBarField(80)
  StatusBarText(0, 4, "Total Matches", #PB_StatusBar_Raised)
  AddStatusBarField(50)
  StatusBarText(0, 5, "Label", #PB_StatusBar_Center | #PB_StatusBar_Raised)
  ListIconGadget(#lstResults, 5, 4, 1035, 304, "File", 300, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect)
  AddGadgetColumn(#lstResults, 1, "Line", 60)
  AddGadgetColumn(#lstResults, 2, "Text", 700)
  CheckBoxGadget(#chkCloseOnSelect, 890, 310, 150, 17, "Close on Double Click")
EndProcedure



;}

;- ===[ Code... ]===

;- ---[ Structures ]---
Structure SearchItem
  len.c
  when.q
  text.s
EndStructure
Structure winPosition
  top.l
  left.l
EndStructure
Structure winsPositions
  search.winPosition
  result.winPosition
EndStructure

;- ---[ Enumerations ]---
Enumeration menus
  #menuResultsList
EndEnumeration
Enumeration menuItems     ;-Menu/hotkey items
  #Menu_CloseWindow 
  #Menu_Search  
  #Menu_ExactMatch
  #Menu_CaseSpecific
  #Menu_IgnoreComment
  #Menu_SearchFor
  #Menu_SearchEverywhere
  #Menu_SearchcASM
  #Menu_SearchIgnorecASM
  
  #menu_OpenFile
  #menu_OpenFolder
  #menu_CloseOnSelect
EndEnumeration
Enumeration               ; option group stuff
  #s_Everything
  #s_cASMonly
  #s_PBonly
EndEnumeration
EnumerationBinary 1       ; for flags in saving options
  #opxcasespecific
  #opxexactmatch
  #opxignorecomment
  #opxignorequotes
  
  #opxEverywhere
  #opxcASM
  #opxpbonly
  
  #opxLstCloseOnSelect
EndEnumeration
Enumeration               ; xml data
  #xml
EndEnumeration
Enumeration               ; files
  #fileSource
  #fileLock
  #fileDB
EndEnumeration
Enumeration FormImage     ; images/icons
  #imgSearchICO
EndEnumeration
Enumeration               ; Regular Expressions
  #regxSearch
  #regxFiles
EndEnumeration

;- ---[ Constants ]---
#useADS             = #True     ; only valid for windows, just to keep the disk tidy.
#maxSaveSearchItems = 20
#regex_notinquotes$             = ~"(?=(?:[^\"]*\"[^\"]*\")*[^\"]*\\Z)" ; regex for ignoring things in quotes
#regex_ExtractFilenamesFromXML  = ~"(?im)(?<=<file name=\\\").*(?=\\\">)"
CompilerIf #PB_Compiler_Unicode ;- maximum path length
  #PATH_MAX = 32767
CompilerElse
  #PATH_MAX = #MAX_PATH
CompilerEndIf
CompilerSelect #PB_Compiler_OS  ;- additional flags for MessageRequester()
  CompilerCase #PB_OS_Windows
    #MR_flags=#MB_TOPMOST|#MB_SYSTEMMODAL
  CompilerCase #PB_OS_Linux ; todo
    #MR_flags=0
    CompilerWarning("a flag to get message requester to be modal is needed for this OS")
CompilerEndSelect

;- ---[ data section ]---
DataSection ; BOM
  bom:
  Data.b $EB,$C,$DC,$A1,$02
EndDataSection

;- ---[ Generic Macros ]---
Macro ods(     c )        : OutputDebugString_( c ) : Debug c               : EndMacro
Macro warning( c )        : ods("*WARNING: " +  c ) : DebuggerWarning( c )  : EndMacro
Macro error(   c )        : ods("** ERROR: " +  c ) : DebuggerWarning( c )  : EndMacro
Macro iifs( b, t, f )     : PeekS( iif(b,@t,@f) )                           : EndMacro
Macro moveWindow( w,x,y ) : ResizeWindow( w, x, y, #PB_Ignore,#PB_Ignore )  : EndMacro
CompilerIf #PB_Compiler_Debugger
  ; non-blocking (ie: in race condition, you can stil pause the app in the debugger.
  Macro LockMutex(     m ) : While Not TryLockMutex( m ) : Delay( 1 ) : Wend : EndMacro
  Macro WaitSemaphore( s ) : While Not TrySemaphore( s ) : Delay( 1 ) : Wend : EndMacro
CompilerEndIf

CompilerSelect #PB_Compiler_OS ;- System wide mutex (lock only 1 instance)
  CompilerCase #PB_OS_Windows
    Macro createSystemMutex(  mutexName ) : CreateMutex_(#NUL,#False,mutexName  )                            : EndMacro
    Macro tryLockSystemMutex( hMutex )    : Bool(WaitForSingleObject_( hMutex,1 )         = #WAIT_OBJECT_0 ) : EndMacro
    Macro lockSystemMutex(    hMutex )    : Bool(WaitForSingleObject_( hMutex,#INFINITE ) = #WAIT_OBJECT_0 ) : EndMacro
    Macro unlockSystemMutex(  hMutex )    : ReleaseMutex_(             hMutex   )                            : EndMacro
    Macro freeSystemMutex(    hMutex )    : CloseHandle_(              hMutex   )                            : EndMacro    
  CompilerDefault 
    CompilerWarning("System wide mutex is needed for this OS")
    CompilerWarning("These are completely untested, just speculative")
    Macro createSystemMutex(  mutexName ) : CreateFile(#fileLock, GetTemporaryDirectory()+"\pbProjectSearch.lock",#PB_File_NoBuffering) : EndMacro
    Macro tryLockSystemMutex( hMutex )    : Bool(IsFile(#fileLock) Or CreateFile(#fileLock, GetTemporaryDirectory()+"\pbProjectSearch.lock",#PB_File_NoBuffering)) : EndMacro
    Macro lockSystemMutex(    hMutex )    : While Not tryLockSystemMutex(#NUL) : Delay( 1 ) : Wend                                      : EndMacro
    Macro unlockSystemMutex(  hMutex )    : CloseFile(#fileLock)                                                                        : EndMacro
    Macro freeSystemMutex(    hMutex )    : DeleteFile(GetTemporaryDirectory()+"\pbProjectSearch.lock")                                 : EndMacro    
CompilerEndSelect

;- ---[ Generic Procedures ]---
;{
Procedure.s getDirectoryEntry( file.s ) ; Fetches the filename as it appears on disk (capitilzations) - SLOW.
  Protected here.s, dir
  
  dir = ExamineDirectory(#PB_Any, GetPathPart(file), GetFilePart(file))
  If dir
    If NextDirectoryEntry(dir)
      here = GetCurrentDirectory()
      SetCurrentDirectory( GetPathPart(file))
      file = GetCurrentDirectory()+DirectoryEntryName(dir)
      SetCurrentDirectory(here)
    EndIf
    FinishDirectory(dir)
  EndIf
  ProcedureReturn file
EndProcedure

Procedure.s getFullPathName( cPath.s, bExact = #False  ) ; gets fullpath from partial or relative paths
  Protected cFullPath.s, *Buffer, nSize, *FilePart
  *Buffer = AllocateMemory(#PATH_MAX * SizeOf(Character))
  If *Buffer
    nSize = GetFullPathName_(@cPath,#PATH_MAX,*Buffer,@*FilePart)
    If nSize : cFullPath = PeekS(*Buffer,nSize) : EndIf
    FreeMemory(*Buffer)
  Else
    Error("Alloc failure")
  EndIf
  If bExact :   cFullPath = GetDirectoryEntry( cFullPath ) : EndIf 
  ProcedureReturn cFullPath
EndProcedure

Procedure   restoreWindow( title.s, class.s="" )
  Protected hWnd,
            bRestored = #False
  
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      hwnd      = FindWindow_( @class, @title )
      bRestored = Bool( hWnd                                      And 
                        IsWindowVisible_(     hWnd )              And
                        ShowWindowAsync_(     hWnd, #SW_RESTORE ) And 
                        SetForegroundWindow_( hWnd              )     )
      SetActiveWindow_( hWnd )
    CompilerDefault
      CompilerWarning("a restoreWindow() method is needed for this OS")
      bRestored=#True
  CompilerEndSelect
  
  ProcedureReturn bRestored
EndProcedure

Procedure   fixTextHK(gadget)
  Protected p, c.s, tmp.s
  
  If GadgetType(gadget)=#PB_GadgetType_Text
    tmp = GetGadgetText( gadget )
    p   = FindString( tmp, "&"  )
    If p
      c = Mid( tmp, p+1, 1)
      ReplaceString( tmp, "&"+c, c+Chr($332), #PB_String_CaseSensitive|#PB_String_InPlace, p ,1 )
      SetGadgetText( gadget, tmp)
    EndIf  
  EndIf 
EndProcedure

Procedure   setWindowIcon( window, image )
  If IsWindow(window) And IsImage(image)
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
        SendMessage_(         WindowID(window), #WM_SETICON, 0, ImageID(image) )
      CompilerCase #PB_OS_Linux
        gtk_window_set_icon_( WindowID(window),                 ImageID(image) )
    CompilerEndSelect
  EndIf
EndProcedure

Procedure   min(a,b)
  CompilerIf #PB_Compiler_Backend=#PB_Backend_C
    ! return( fmin(v_a,v_b) );//(v_a<v_b?v_a:v_b);
  CompilerElse
    If a<b : ProcedureReturn a
    Else   : ProcedureReturn b
    EndIf
  CompilerEndIf
EndProcedure

Procedure   iif( b, t, f )
  CompilerIf #PB_Compiler_Backend = #PB_Backend_C
    ! return( (v_b?v_t:v_f) );
  CompilerElse
    If b : ProcedureReturn t : EndIf
    ProcedureReturn f
  CompilerEndIf
EndProcedure

Procedure   waitForFile(f, waitMS=#INFINITE)
  Protected s 
  If IsFile(f)
    If waitMS <> #INFINITE : s = ElapsedMilliseconds() : EndIf 
    While IsFile(f) And (waitMS=#INFINITE Or waitMS > (ElapsedMilliseconds()-s) )
      Delay( 1 )
    Wend
  EndIf 
  ProcedureReturn Bool( Not IsFile(f ) )
EndProcedure
;}

;- ---[ Project Search Procedures ]---
;{
Procedure.s readToString( file.s )
  Protected fdata.s, s, c.c, encoding
  
  If ReadFile(#fileSource, file, #PB_File_SharedRead)
    encoding = ReadStringFormat( #fileSource )
    fdata = ReadString(#fileSource, #PB_File_IgnoreEOL|encoding, Lof(#fileSource)) 
  Else
    ods("Failed to read file: "+file)
  EndIf
  ProcedureReturn fdata
EndProcedure

Procedure   getLineNo( source.s, instance )
  Protected tmp.s, x, l,
            encoding, count, lineNo,
            caseSpecific = GetGadgetState( #chkCaseSpecific )
  
  source=Trim(source) : l=Len(source)
  If Not caseSpecific : source = LCase( source ) : EndIf 
  
  FileSeek( 0,0, #PB_Absolute )
  encoding =ReadStringFormat(#fileSource)
  
  instance+1
  For x = 1 To instance
    While Not Eof(#fileSource)
      count + 1
      tmp = ReadString( #fileSource, encoding )
      tmp = Left(Trim(tmp),l)
      If (caseSpecific And tmp = source) Or LCase(tmp) = source
        lineno = count
        Break
      EndIf
    Wend 
  Next
  ProcedureReturn lineNo
EndProcedure

Procedure   searchFile( cFile.s )
  Protected Dim results.s(0)
  Protected regX.s, fileStr.s,
            x, t, p, lineno,
            matches.i,
            commentPosition,
            SearchWhat,
            rxFlags         = #PB_RegularExpression_AnyNewLine|#PB_RegularExpression_MultiLine,
            bCaseSpecific   = GetGadgetState( #chkCaseSpecific   ), 
            bExactMatch     = GetGadgetState( #chkExact          ), 
            bIgnoreComments = GetGadgetState( #chkIgnoreComments ),
            textToFind.s    = GetGadgetText(  #cboSearchFor      ),
            bIgnoreQuotes   = GetGadgetState( #chkIgnoreQuotes   )
  
  If Not bCaseSpecific : rxFlags|#PB_RegularExpression_NoCase : EndIf
  
  #pcre_needsEscaping="\%.*+-()[]{}<>|^$?"
  For x = 1 To Len( #pcre_needsEscaping )
    textToFind = ReplaceString( textToFind, Mid(#pcre_needsEscaping,x,1),"\"+Mid(#pcre_needsEscaping,x,1) )
  Next 
  
  If     bExactMatch   : regx+"^.*(\b"+textToFind+"\b)" ; find the string, as a whole word
  Else                 : regx+"^.*("  +textToFind+  ")" ; find the string, somewhere, in the line
  EndIf
  If     bIgnoreQuotes : regx+#regex_notinquotes$ : EndIf 
  
  regx+".*$" ; but only across a line (start-of-line *text* end-of-line)
  
  If Not CreateRegularExpression(#regxSearch, regx, rxFlags)
    MessageRequester("Error","Invalid Regex"+#CRLF$+regx+#CRLF$+RegularExpressionError(),#PB_MessageRequester_Error)
    End
  EndIf
  
  ; it got too complicated to handle all the combinations in a single regex.
  If GetGadgetState(#optcASM)
    SearchWhat=#s_cASMonly ; starts with "!" (outside of quotes)
  ElseIf GetGadgetState(#optPBOnly)
    SearchWhat=#s_PBonly   ; does not start with "!" (outside of quotes)
  Else                     ;If GetGadgetState(#optEverywhere)
    SearchWhat=#s_Everything
  EndIf
  
  ods("Scanning "+cFile)
  fileStr = readToString( cFile )
  If Len(fileStr) > 0
    t = ExtractRegularExpression(#regxSearch,fileStr,results()) 
    ods("Matches: "+Str(t))
    t-1
    Protected bAddit
    ; before comment
    For x = 0 To t
      bAddit=#True 
      ; This needs updating, right now the matching is super simple, "line starts with..."but this doesn't account for ":" 
      If bIgnoreComments And Left(Trim(results(x)),1)=";"
        ;commentPosition=FindString(results(x),";",0,#PB_String_CaseSensitive)
        ;If commentPosition And (FindString(results(x),textToFind,0,#PB_String_NoCase) > commentPosition)
        ods("Comment, not adding: "+results(x))
        bAddit=#False
        ;EndIf
      ElseIf SearchWhat=#s_cASMonly And Left(Trim(results(x)),1)<>"!"
        ods("PB code, not adding: "+results(x))
        bAddit=#False
      ElseIf searchwhat=#s_PBonly   And Left(Trim(results(x)),1)="!"
        ods("ASM/C code, not adding: "+results(x))
        bAddit=#False
      EndIf 
      
      If bAddit
        ods("Adding ("+lineno+") "+results(x))
        ;todo lineno not updating!
        lineno = getLineNo(results(x), x+1)
        AddGadgetItem(#lstResults,x,cFile + #LF$ + Str(lineno) + #LF$ + Trim( results(x) ) )
        matches+1
      EndIf
    Next
    FreeRegularExpression(#regxSearch)  
  Else
    ods("empty file")
  EndIf
  If IsFile(#fileSource) : CloseFile(#fileSource) : EndIf 
  ProcedureReturn matches
EndProcedure

Procedure   scanProject( cProjectFile.s )
  Protected str.s,x,t, matchCount, fileCount, tmp
  Dim results.s(0)
  
  str = readToString( cProjectFile ) : CloseFile(#fileSource)
  If str<>""
    ;- Regex to pull out any file names that are included in project file or source.
    CreateRegularExpression(#regxFiles,#regex_ExtractFilenamesFromXML)
    t = ExtractRegularExpression(#regxFiles, str, results() ) : t-1
    For x = 0 To t  
      results(x) = getFullPathName( results(x), #True )
      tmp = searchFile( results(x) ) 
      If tmp
        fileCount+1
        matchCount+tmp
      EndIf 
    Next 
    
    StatusBarText(0, 1, Str(t),          #PB_StatusBar_Center )
    StatusBarText(0, 3, Str(fileCount),  #PB_StatusBar_Center )
    StatusBarText(0, 5, Str(matchCount), #PB_StatusBar_Center )
    
    FreeRegularExpression(#regxFiles)
  EndIf 
  ProcedureReturn CountGadgetItems(#lstResults)
EndProcedure

Procedure.s getTitleXML( cProjectFile.s )
  Protected cTitle.s, *x
  If FileSize(cProjectFile) > 0 And LoadXML(#XML, cProjectFile)
    If XMLStatus(#XML) <> #PB_XML_Success
      Warning("XML Error: " + XMLError(#XML) + "Line: " + Str(XMLErrorLine(#XML)) + "   Character: " + Str(XMLErrorPosition(#XML)))
    Else 
      *x = MainXMLNode(#XML)
      *x = XMLNodeFromPath(           *x,"/project/section/options")
      If ExamineXMLAttributes(        *x )
        While NextXMLAttribute(       *x )
          If XMLAttributeName(        *x ) = "name"
            cTitle=XMLAttributeValue( *x )
            Break
          EndIf
        Wend
      EndIf
    EndIf 
    FreeXML(#xml)
  EndIf
  ProcedureReturn cTitle
EndProcedure
;}

;- ---[ Threaded Procedures ]---
;{
CompilerIf Not #PB_Compiler_Thread
  CompilerWarning("Advise: Use ThreadSafe!")
CompilerEndIf

Procedure loadDB(hSemaphore)
  Protected *p.SearchItem, 
            winpos.winsPositions,
            i, t, flags.c, dbName.s
  
  waitForFile(#fileDB)
  
  dbName = GetEnvironmentVariable("PB_TOOL_PROJECT")+".search.db"
  CompilerIf #useADS And #PB_Compiler_OS=#PB_OS_Windows
    ReplaceString(dbname,"\",".",#PB_String_InPlace)
    ReplaceString(dbname,":",".",#PB_String_InPlace)
    dbname=ProgramFilename()+":"+dbName ; use ads to keep from cluttering disk
  CompilerEndIf
  If ReadFile(#fileDB, dbName, #PB_Unicode)
    *p=AllocateMemory(SizeOf(SearchItem))
    ReadData(#fileDB,*p,5)
    If CompareMemory(*p,?bom,5)
      t = ReadInteger(#fileDB) : flags=ReadCharacter(#fileDB) : ReadData(#fileDB,@winpos,SizeOf(winsPositions))
      
      moveWindow(#winResults,winPos\result\left,winPos\result\top)
      moveWindow(#winSearch,winPos\search\left,winPos\search\top)
      
      SignalSemaphore(hSemaphore)
      
      Dim items.SearchItem(t)
      i=0
      While Not Eof(#fileDB) And i <= t
        ReadData(#fileDB,*p,SizeOf(SearchItem))
        items(i)\len  = *p\len
        items(i)\when = *p\when
        items(i)\text = ReadString(#fileDB,#PB_Unicode,items(i)\len)
        i+1
      Wend
      
      SortStructuredArray(items(),#PB_Sort_Descending,OffsetOf(SearchItem\text),#PB_String)
      For i = 0 To t            
        AddGadgetItem(#cboSearchFor,i,Trim(items(i)\text))
        SetGadgetItemData(#cboSearchFor,i,items(i)\when)
      Next
      
      SetGadgetState( #chkCaseSpecific,   flags&#opxcasespecific     )
      SetGadgetState( #chkExact,          flags&#opxexactmatch       )
      SetGadgetState( #chkIgnoreComments, flags&#opxignorecomment    )
      SetGadgetState( #chkIgnoreQuotes,   flags&#opxignorequotes     )
      SetGadgetState( #optcASM,           flags&#opxcASM             )
      SetGadgetState( #optEverywhere,     flags&#opxEverywhere       )
      SetGadgetState( #optPBOnly,         flags&#opxpbonly           )
      SetGadgetState( #chkCloseOnSelect,  flags&#opxLstCloseOnSelect )
      
    Else
      Warning("Invalid BOM")
      SignalSemaphore( hSemaphore )
    EndIf
    FreeMemory(*p)
    CloseFile(#fileDB)
  Else 
    ods("No db to open")
    SignalSemaphore( hSemaphore )
  EndIf
EndProcedure

Procedure saveDB(lock)
  Protected bAlready, i, flags.c, dbName.s,
            winPos.winsPositions
  
  LockMutex(lock)
  Protected t = CountGadgetItems( #cboSearchFor )
  Protected Dim items.SearchItem( t )
  
  If GetGadgetState(#chkCaseSpecific)   : flags|#opxcasespecific     : EndIf
  If GetGadgetState(#chkExact)          : flags|#opxexactmatch       : EndIf 
  If GetGadgetState(#chkIgnoreComments) : flags|#opxignorecomment    : EndIf
  If GetGadgetState(#chkIgnoreQuotes)   : flags|#opxignorequotes     : EndIf
  If GetGadgetState(#chkCloseOnSelect)  : flags|#opxLstCloseOnSelect : EndIf 
  If     GetGadgetState(#optcASM)       : flags|#opxcASM           
  ElseIf GetGadgetState(#optPBOnly )    : flags|#opxpbonly         
  Else                                  : flags|#opxEverywhere    
  EndIf
  
  t-1
  For i = 0 To t
    items(i)\text = GetGadgetItemText(#cboSearchFor,i)
    items(i)\when = GetGadgetItemData(#cboSearchFor,i)
    items(i)\len  = Len(items(i)\text)
    If items(i)\text=GetGadgetText(#cboSearchFor)
      SetGadgetState(#cboSearchFor,i)
      SetGadgetItemData(#cboSearchFor,i,Date())
      bAlready=#True
    EndIf 
  Next
  
  If Not bAlready
    t+1
    items(t)\text = GetGadgetText(#cboSearchFor)
    items(t)\when = Date()
    items(t)\len  = Len(items(t)\text)
    ClearGadgetItems(#cboSearchFor)
    SortStructuredArray(items(),#PB_Sort_Descending,OffsetOf(SearchItem\text),#PB_String)
    For i = 0 To min(t,#maxSaveSearchItems)
      AddGadgetItem(#cboSearchFor,i,items(i)\text)
      SetGadgetItemData(#cboSearchFor,i,items(i)\when)
      If items(i)\text=GetGadgetText(#cboSearchFor)
        SetGadgetState(#cboSearchFor,i)
      EndIf
    Next
  EndIf 
  SortStructuredArray(items(),#PB_Sort_Descending,OffsetOf(SearchItem\when),#PB_Quad)
  waitForFile(#fileDB)
  
  dbName = GetEnvironmentVariable("PB_TOOL_PROJECT") + ".search.db"
  CompilerIf #useADS And #PB_Compiler_OS=#PB_OS_Windows
    ReplaceString(dbname,"\",".",#PB_String_InPlace)
    ReplaceString(dbname,":",".",#PB_String_InPlace)
    dbname=ProgramFilename()+":"+dbName ; use ads to keep from cluttering disk
  CompilerEndIf
  
  If CreateFile(#fileDB, dbName, #PB_Unicode)
    WriteData(#fileDB,?bom,5) : WriteInteger(#fileDB,min(t,#maxSaveSearchItems)) : WriteCharacter(#fileDB,flags)
    winPos\search\left=WindowX( #winSearch,   #PB_Window_FrameCoordinate )
    winPos\search\top=WindowY(  #winSearch,   #PB_Window_FrameCoordinate )
    winPos\result\left=WindowX( #winResults,  #PB_Window_FrameCoordinate )
    winPos\result\top=WindowY(  #winResults,  #PB_Window_FrameCoordinate )
    WriteData(#fileDB,@winpos,SizeOf(winsPositions))
    
    For i = 0 To min(t,#maxSaveSearchItems)
      WriteData(   #fileDB, @items(i), SizeOf(SearchItem) )
      WriteString( #fileDB,  items(i)\text, #PB_Unicode   )
    Next
    CloseFile(#fileDB)
  Else
    Warning("** Failed to write search db")
  EndIf    
  
  UnlockMutex(lock)
EndProcedure
;}

;- ---[ Main Processing ]---
Procedure main()
  Protected ev,gn, m, s, cFileName.s, cLineNumber.s, cTitle.s,
            hSysMutex = createSystemMutex( "pbProjectSearch" ),
            pbHome.s  = ProgramParameter(0),
            project.s = GetEnvironmentVariable("PB_TOOL_PROJECT")
  
  CompilerIf #PB_Compiler_Debugger
    project = "c:\pbincludes\josh\test-timer.pbp"
    pbHome  = #PB_Compiler_Home
  CompilerEndIf
  
  If FileSize(pbHome)=-2 And project <> ""
    If tryLockSystemMutex( hSysMutex ) ; Suggested by highend to stop double-runs
      OpenwinSearch()  : OpenwinResults() 
      
      s=CreateSemaphore() 
      CompilerIf #PB_Compiler_Thread
        CreateThread( @loadDB(), s )
      CompilerElse 
        loadDB( s )
      CompilerEndIf 
      CatchImage(    #imgSearchICO, ?beginSearchico, ?endSearchico-?beginSearchico )
      setWindowIcon( #winSearch,    #imgSearchICO ) : StickyWindow( #winSearch,  #True ) : fixTextHK(#txtSearchFor) 
      setWindowIcon( #winResults,   #imgSearchICO ) 
      
      If CreatePopupMenu( #menuResultsList         )
        MenuItem( #menu_CloseOnSelect, "Close Search on Select")
        MenuItem( #menu_openFile,   "Open File"   )
        MenuItem( #menu_openFolder, "Open Folder" )
        SetMenuItemState( #menuResultsList, #menu_CloseOnSelect, GetGadgetState(#chkCloseOnSelect) ) 
      EndIf
      
      cTitle=getTitleXML(project)
      If cTitle
        SetWindowTitle( #winSearch,  "Search: "  + cTitle )
        SetWindowTitle( #winResults, "Results: " + cTitle )
      EndIf 
      
      HideWindow(#winSearch,#False)
      SetActiveGadget(#cboSearchFor)
      
      m=CreateMutex()
      SetCurrentDirectory( GetPathPart(project))
      
      ;{ Add keyboard shortcuts.
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Return,             #Menu_Search)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Escape,             #Menu_CloseWindow)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Alt|#PB_Shortcut_E, #Menu_ExactMatch)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Alt|#PB_Shortcut_C, #Menu_CaseSpecific)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Alt|#PB_Shortcut_I, #Menu_IgnoreComment)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Alt|#PB_Shortcut_S, #Menu_Search)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Alt|#PB_Shortcut_F, #Menu_SearchFor)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Alt|#PB_Shortcut_A, #Menu_SearchEverywhere)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Alt|#PB_Shortcut_M, #Menu_SearchcASM)
      AddKeyboardShortcut(#winSearch,  #PB_Shortcut_Alt|#PB_Shortcut_G, #Menu_SearchIgnorecASM)
      AddKeyboardShortcut(#winResults, #PB_Shortcut_Escape,             #Menu_CloseWindow)
      AddKeyboardShortcut(#winResults, #PB_Shortcut_Return,             #menu_OpenFile)
      ;}
      CompilerIf #PB_Compiler_Thread
        ; when using a thread to resize a window, it requires there to be an event loop; no  thread, no loop needed.
        Repeat
          WaitWindowEvent(0)
        Until TrySemaphore(s)
      CompilerEndIf
      FreeSemaphore(s)
      
      Repeat
        Select WaitWindowEvent(1)
          Case #PB_Event_Menu
            Select EventMenu()
              Case #Menu_Search
                PostEvent(#PB_Event_Gadget,#winSearch,#btnSearch,#PB_EventType_LeftClick)
              Case #Menu_CloseWindow 
                Select GetActiveWindow()
                  Case #winSearch
                  Break
                Case #winResults
                  HideWindow(#winResults,#True)
                  HideWindow(#winSearch,#False)
                  DisableWindow(#winSearch,#False)
                EndSelect
              Case #Menu_SearchFor        : SetActiveGadget(#cboSearchFor)
              Case #Menu_ExactMatch       : SetGadgetState( #chkExact,          Bool(Not GetGadgetState( #chkExact          )) )
              Case #Menu_CaseSpecific     : SetGadgetState( #chkCaseSpecific,   Bool(Not GetGadgetState( #chkCaseSpecific   )) )
              Case #Menu_IgnoreComment    : SetGadgetState( #chkIgnoreComments, Bool(Not GetGadgetState( #chkIgnoreComments )) )
              Case #Menu_SearchEverywhere : SetGadgetState( #optEverywhere, #True )
              Case #Menu_SearchcASM       : SetGadgetState( #optcASM,       #True )
              Case #Menu_SearchIgnorecASM : SetGadgetState( #optPBOnly,     #True )
                
              Case #menu_OpenFile
                ; fix by highend (quote around file name)
                cFileName   = GetGadgetItemText(#lstResults,GetGadgetState(#lstResults),0)
                cLineNumber = GetGadgetItemText(#lstResults,GetGadgetState(#lstResults),1)
                RunProgram( pbHome+"PureBasic.exe",~"\""+cFileName+~"\" /L "+cLineNumber,GetPathPart(project)) 
                If GetGadgetState(#chkCloseOnSelect)
                  PostEvent(#PB_Event_CloseWindow,#winResults,#winResults)
                  PostEvent(#PB_Event_CloseWindow,#winSearch,#winSearch)
                EndIf 
              Case #menu_OpenFolder
                RunProgram("explorer.exe",~"/select,\"" + cFileName  + ~"\"","")
                If GetGadgetState(#chkCloseOnSelect)
                  PostEvent(#PB_Event_CloseWindow,#winResults,#winResults)
                  PostEvent(#PB_Event_CloseWindow,#winSearch,#winSearch)
                EndIf 
              Case #menu_CloseOnSelect
                SetMenuItemState(#menuResultsList, #menu_CloseOnSelect, Bool( Not GetMenuItemState( #menuResultsList, #menu_CloseOnSelect ) ) )
                SetGadgetState( #chkCloseOnSelect, GetMenuItemState( #menuResultsList, #menu_CloseOnSelect ) )
            EndSelect
            
          Case #PB_Event_SizeWindow,#PB_Event_MoveWindow
            CompilerIf #PB_Compiler_Thread
              CreateThread( @saveDB(),m)
            CompilerElse
              saveDB(m)
            CompilerEndIf
          Case #PB_Event_CloseWindow 
            Select EventWindow()
              Case #winSearch
                Break 
              Case #winResults
                HideWindow(    #winResults, #True  )
                StickyWindow(  #winResults, #False )
                HideWindow(    #winSearch,  #False )
                DisableWindow( #winSearch,  #False )
                StickyWindow(  #winSearch,  #True  )
            EndSelect
          Case #PB_Event_Gadget 
            Select EventGadget()
              Case #chkCloseOnSelect
                SetMenuItemState( #menuResultsList, #menu_CloseOnSelect, GetGadgetState(#chkCloseOnSelect) ) 
                Select EventType()
                  Case #PB_EventType_Change
                    CompilerIf #PB_Compiler_Thread
                      CreateThread( @saveDB(),m)
                    CompilerElse
                      saveDB(m)
                    CompilerEndIf
                EndSelect
              Case #lstResults
                Select EventType()
                  Case #PB_EventType_RightClick
                    DisplayPopupMenu(0, WindowID(#winResults))  ; now display the popup-menu        
                  Case  #PB_EventType_LeftDoubleClick
                    PostEvent(#PB_Event_Menu,#winResults,#menu_OpenFile)
                EndSelect
              Case #btnSearch
                DisableWindow(#winSearch,#True)
                CompilerIf #PB_Compiler_Thread
                  CreateThread( @saveDB(),m)
                CompilerElse
                  saveDB(m)
                CompilerEndIf
                ClearGadgetItems(#lstResults)
                If scanProject( project )
                  HideWindow(    #winSearch,  #True  )
                  DisableWindow( #winSearch,  #True  )
                  StickyWindow(  #winSearch,  #False )
                  HideWindow(    #winResults, #False )
                  StickyWindow(  #winResults, #True  )
                Else
                  MessageRequester("Project Search","No matches found",#PB_MessageRequester_Ok|#MR_flags,WindowID(#winSearch))
                  HideWindow(    #winSearch,  #False )
                  DisableWindow( #winSearch,  #False )
                  StickyWindow(  #winSearch,  #True  )
                  HideWindow(    #winResults, #True  )
                  StickyWindow(  #winResults, #False )
                EndIf     
              Case #cboSearchFor  
                Select EventType()
                  Case #PB_EventType_Change,#PB_EventType_LostFocus
                    DisableGadget(#btnSearch, Bool(Len(Trim(GetGadgetText(#cboSearchFor)))=0))
                  Case #PB_EventType_Focus
                    CompilerSelect #PB_Compiler_OS
                      CompilerCase #PB_OS_Windows
                        SendMessage_( GadgetID(#cboSearchFor), #EM_SETSEL, 0, -1)
                      CompilerDefault
                        CompilerWarning("a method to select all gadget text is needed for this OS")
                    CompilerEndSelect
                EndSelect
            EndSelect
        EndSelect
      ForEver 
      unlockSystemMutex(hSysMutex)
    Else 
      MessageRequester("Project Search","Already Running",#PB_MessageRequester_Ok|#MR_flags)
      CompilerSelect #PB_Compiler_OS
        CompilerCase #PB_OS_Windows
          If Not restoreWindow( "Project Search",         "WindowClass_0" )
            restoreWindow(      "Project Search Results", "WindowClass_1" )
          EndIf 
        CompilerDefault
          CompilerWarning("a method to restore focus to windows is needed for this OS")
      CompilerEndSelect
    EndIf 
  Else
    MessageRequester("Project Search","No project active",#PB_MessageRequester_Ok|#MR_flags)
  EndIf 
  freeSystemMutex(hSysMutex)
EndProcedure

main() ;- Run the program


;- ---[ Icon Data Section ]---
DataSection
  beginSearchico:
  Data.w $0000,$0001,$0001,$3030,$0000,$0001,$0008,$0EA8, $0000,$0016,$0000,$0028,$0000,$0030,$0000,$0060
  Data.w $0000,$0001,$0008,$0000,$0000,$0000,$0000,$0EC4, $0000,$0EC4,$0000,$0000,$0000,$0000,$0000,$FFFF
  Data.w $00FF,$F3F3,$00F3,$E9E9,$00E9,$E4E4,$00E4,$C9C9, $00C9,$C5C5,$00C5,$DEFB,$00BB,$DCFA,$00B5,$D7FB
  Data.w $00AC,$D3FA,$00A4,$D0F9,$009D,$CEF9,$009A,$CCF9, $0094,$C9F8,$008F,$ABAB,$00AB,$A9AB,$00A6,$A6A8
  Data.w $00A2,$A1A1,$00A1,$A0A2,$009B,$9EA0,$0099,$9E9E, $009E,$999C,$0093,$9699,$0090,$8383,$0083,$C0F7
  Data.w $007B,$BFF7,$0079,$BCF7,$0073,$B8F7,$006A,$B6F6, $0068,$B5F6,$0064,$B1EF,$0064,$A9E0,$0063,$A8DE
  Data.w $0063,$A1D1,$0064,$9ECE,$0063,$92B9,$0063,$91B7, $0063,$88A7,$0063,$87A5,$0062,$7482,$0062,$7B7B
  Data.w $007B,$7272,$0072,$727E,$0062,$6E79,$0061,$6D77, $0062,$6C6C,$006C,$676B,$0062,$6162,$0061,$6267
  Data.w $0058,$6267,$0057,$5F64,$0054,$4F54,$0044,$4850, $0038,$474F,$0038,$474F,$0037,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
  Data.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, $0000,$0000,$0000,$0000,$0000,$0000,$0000,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3115,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$1537,$3636,$0132,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3616,$3636,$3036,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$1537,$3636,$3636,$1336,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3515,$3636,$3636,$3713,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $1537,$3636,$3636,$1336,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3515,$3636,$3636,$3713,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$1537, $3636,$3636,$1336,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$0401,$1711, $2F2D,$2D2F,$1117,$0104,$3737,$3737,$3737,$3611
  Data.w $3636,$3636,$3712,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$1101,$2F2D,$2F2F
  Data.w $2F2F,$2F2F,$2F2F,$2D2F,$0211,$3737,$0437,$332F, $3636,$1336,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$0E37,$2F2D,$2F2F,$2F2F, $2F2F,$2F2F,$2F2F,$2F2F,$2F2F,$370E,$2F04,$2F2F
  Data.w $3633,$3713,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$0137,$2F17,$2F2F,$2C2F,$2226
  Data.w $1E20,$201E,$2622,$2F2C,$2F2F,$282F,$2F2F,$2F2F, $0F2F,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$2903,$2F2F,$2B2F,$1D22,$1D1D, $1D1D,$1D1D,$1D1D,$221D,$2F2A,$2F2F,$2F2F,$2F2F
  Data.w $3705,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$0237,$2F29,$2F2F,$1D24,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$241D,$2F2F,$2F2F,$052F, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$1737,$2F2F,$212E,$1D1D,$1D1D,$1D1D, $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2E22,$2F2F,$3728
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$2F0E,$2F2F,$1D22,$1D1D,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$211D,$2F2F,$0E2F, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$0337,$2F2F,$232F,$1D1D,$1D1D,$1D1D,$1D1D, $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F24,$2F2F
  Data.w $3702,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$1337,$2F2F,$1D2A,$1D1D,$1D1D,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2A1D,$2F2F, $3711,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$2D01,$2F2F,$1D22,$1D1D,$1D1D,$1D1D,$1D1D, $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$221D,$2F2F
  Data.w $372F,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$2F04,$2C2F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F2C, $042F,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$2F13,$262F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D, $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F26
  Data.w $112F,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$2F17,$222F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F22, $172F,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$2F2D,$202F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D, $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F20
  Data.w $2D2F,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$2F2F,$1E2F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F1E, $2F2F,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$2F2F,$1E2F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D, $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F1E
  Data.w $2F2F,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$2F2D,$202F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F20, $2D2F,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$2F17,$222F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D, $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F22
  Data.w $172F,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$2F13,$262F,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2F26, $112F,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$2F04,$2B2F,$1D1D,$1D1D,$091D,$1D0C,$1D1D, $1D1D,$1D1D,$1D1D,$0C1D,$1D09,$1D1D,$1D1D,$2F2C
  Data.w $042F,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$2F01,$2F2F,$1D22,$1D1D,$071B,$0A06,$1D1C
  Data.w $1D1D,$1D1D,$1C1D,$060A,$1D07,$1D1D,$221D,$2F2F, $012D,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$1337,$2F2F,$1D27,$1D1D,$191D,$0607,$0D07, $1D1A,$1A1C,$070D,$0706,$1D19,$1D1D,$2A1D,$2F2F
  Data.w $3711,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$0237,$2F2F,$232F,$1D1D,$1D1D,$091A,$0606
  Data.w $0606,$0606,$0606,$1A09,$1D1D,$1D1D,$2F24,$2D2F, $3702,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$2F0F,$2F2F,$1D21,$1D1D,$1D1D,$0A19, $0708,$0807,$190A,$1D1D,$1D1D,$211D,$2F2F,$0E2F
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$2837,$2F2F,$222E,$1D1D,$1D1D,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$1D1D,$1D1D,$2E22,$2F2F,$3717, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$0237,$2F2D,$2F2F,$1D23,$1D1D,$1D1D, $1D1D,$1D1D,$1D1D,$1D1D,$241D,$2F2F,$292F,$3702
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$2D03,$2F2F,$2A2F,$1D22,$1D1D
  Data.w $1D1D,$1D1D,$1D1D,$211D,$2F27,$2F2F,$0329,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$0237,$2F28,$2F2F,$2C2F,$2226, $1E20,$201E,$2622,$2F2B,$2F2F,$172F,$3702,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$0F37,$2F2F,$2F2F,$2F2F
  Data.w $2F2F,$2F2F,$2F2F,$2F2F,$2F2F,$370E,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$1303,$2F2F,$2F2F, $2F2F,$2F2F,$2F2F,$2F2F,$0213,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$0401,$1713
  Data.w $2F2D,$2D2F,$1317,$0104,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737
  Data.w $3737,$3737,$3737,$3737,$3737,$3737,$3737,$3737, $3737,$3737,$3737,$3737,$3737,$3737,$3737,$FFFF
  Data.w $FFFF,$FFFF,$0000,$FFFF,$FFFF,$FFFF,$0000,$FFFF, $FFFF,$FFFF,$0000,$FFFF,$FFFF,$FFFF,$0000,$FFFF
  Data.w $FFFF,$3FFF,$0000,$FFFF,$FFFF,$0FFE,$0000,$FFFF, $FFFF,$0FFC,$0000,$FFFF,$FFFF,$0FF8,$0000,$FFFF
  Data.w $FFFF,$1FF0,$0000,$FFFF,$FFFF,$3FE0,$0000,$FFFF, $FFFF,$7FC0,$0000,$FFFF,$FFFF,$FF80,$0000,$FCFF
  Data.w $3F00,$FF01,$0000,$F0FF,$0E00,$FF03,$0000,$E0FF, $0400,$FF07,$0000,$80FF,$0000,$FF0F,$0000,$00FF
  Data.w $0000,$FF1F,$0000,$00FE,$0000,$FF3F,$0000,$00FE, $0000,$FF7F,$0000,$00FC,$0000,$FF3F,$0000,$00F8
  Data.w $0000,$FF1F,$0000,$00F8,$0000,$FF1F,$0000,$00F0, $0000,$FF1F,$0000,$00F0,$0000,$FF0F,$0000,$00F0
  Data.w $0000,$FF0F,$0000,$00F0,$0000,$FF0F,$0000,$00F0, $0000,$FF0F,$0000,$00F0,$0000,$FF0F,$0000,$00F0
  Data.w $0000,$FF0F,$0000,$00F0,$0000,$FF0F,$0000,$00F0, $0000,$FF0F,$0000,$00F0,$0000,$FF0F,$0000,$00F0
  Data.w $0000,$FF0F,$0000,$00F0,$0000,$FF0F,$0000,$00F8, $0000,$FF1F,$0000,$00F8,$0000,$FF1F,$0000,$00FC
  Data.w $0000,$FF3F,$0000,$00FE,$0000,$FF7F,$0000,$00FE, $0000,$FF7F,$0000,$00FF,$0000,$FFFF,$0000,$80FF
  Data.w $0100,$FFFF,$0000,$E0FF,$0700,$FFFF,$0000,$F0FF, $0F00,$FFFF,$0000,$FCFF,$3F00,$FFFF,$0000,$FFFF
  Data.w $FFFF,$FFFF,$0000,$FFFF,$FFFF,$FFFF,$0000,$FFFF, $FFFF,$FFFF,$0000,$FFFF,$FFFF,$FFFF,$0000
  endSearchico:
EndDataSection
Last edited by jassing on Sat May 25, 2024 2:26 am, edited 9 times in total.
User avatar
idle
Always Here
Always Here
Posts: 5898
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Project Search

Post by idle »

looks useful, thanks.
highend
Enthusiast
Enthusiast
Posts: 169
Joined: Tue Jun 17, 2014 4:49 pm

Re: Project Search

Post by highend »

Thanks!

2 small suggestions:

To not run multiple instances (where each window would cover the one of another instance):
- At the beginning of the main procedure, add:

Code: Select all

  Protected hApp,ev,gn,pbHome.s

  hApp = CreateSemaphore_(0, 0, 1, "{c6b322fb-d1dc-4413-9506-2f0b14914c6f}" + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension))
  If hApp <> 0 And GetLastError_() = #ERROR_ALREADY_EXISTS
    CloseHandle_(hApp)
    End
  EndIf

Replace this line (it won't open files with spaces here otherwise):

Code: Select all

RunProgram( pbHome+"PureBasic.exe",GetGadgetItemText(#lstResults,GetGadgetState(#lstResults),0)+" /L "+GetGadgetItemText(#lstResults,GetGadgetState(#lstResults),1),pbHome )
with

Code: Select all

RunProgram( pbHome+"PureBasic.exe",#DQUOTE$+GetGadgetItemText(#lstResults,GetGadgetState(#lstResults),0)+#DQUOTE$+" /L "+GetGadgetItemText(#lstResults,GetGadgetState(#lstResults),1),pbHome )
jassing
Addict
Addict
Posts: 1885
Joined: Wed Feb 17, 2010 12:00 am

Re: Project Search

Post by jassing »

highend wrote: Tue May 21, 2024 11:44 am 2 small suggestions:
I guess I never tried to launch two copies... Good idea.

Ah. Yes, this is proof of home-grown...I'm old ... so old that "file names with spaces" are "bad.
Code updated. Thanks.

Another thing: this grew out of another project, so I reused some of it (namely the map) - it would be simpler without it. I might see about updating that bit later.
Post Reply