
  '
  '============
  'SEARCH FILES
  '============

  '00:00 22/03/2014
  '20:55 17/03/2017
  '16:00 20/10/2018
  '18:59 03/08/2019
  '13:44 26/03/2022
  '13:44 20/06/2022
  '14:58 17/06/2023
  '
  '
  'Charles E V Pegge

  #compact
  $filename "finded.exe"
 'uses rtl64
 'uses oxide.res
  uses searchutil

  string cfgs="FindEd.cfg"

  'http://msdn.microsoft.com/en-us/library/ms632598(VS.85).aspx
  '
  '
  'when all the keywords are found within close range
  'of each other, the region of text is displayed



  function SetFont(sys *hwnd,n,height,width,weight,string fontname) as sys
  ========================================================================
  indexbase 1
  sys hFont,i
  hfont=CreateFont( height,width,0,0,weight,0,0,0,0,0,0,0,0,fontname)
  for i=1 to n
    SendMessage hwnd(i),WM_SETFONT,hfont,0
  next
  return hFont
  end function

 
  function display(string s,sys a)
  '===============================
  sys b,e
  sample s,a,200,b,e
  print a cr cr mid s,b,e-b
  end function





  % ID_FIRSTCHILD  100 'Each window 100... 


  sys hinstance
  sys hWndMain

  ======================================
  
  sys inst=GetModuleHandle 0
  asciiz * cmdline 
  @cmdline=GetCommandLine

  ======================================


  'STANDARD CHILD WINDOWS STYLES
  '=============================
  '
  'Button	The class for a button.
  'ComboBox	The class for a combo box.
  'Edit	        The class for an edit control.
  'ListBox	The class for a list box.
  'MDIClient	The class for an MDI client window.
  'ScrollBar	The class for a scroll bar.
  'Static	The class for a static control.



  function GetSelTextLine(sys h) as string
  ============================================
  sys b,je,le,st,en
  static string s
  s=nuls 512
  b=strptr(s)
  *b=512 'put buffer length info into buffer string
  SendMessage h, EM_GETSEL, @st, @en       'LOCATE CARET / SELECTED AREA
  je=SendMessage h, EM_LINEFROMCHAR, st, 0 'IDENTIFY LINE
  st=SendMessage h, EM_LINEINDEX, je, 0    'LOCATE START OF LINE
  le=SendMessage h, EM_GETLINE, je, b      'GET LINE STRING AND LENGTH
  SendMessage h,EM_SETSEL,st,st+le         'HIGHLIGHT LINE
  return left s,le                         'RETURN LINE STRING
  end function


  function PostFileContent(string *f,sys hl,hd) as sys
  ====================================================
  sys a
  string s,sl
  f=GetSelTextLine hl
  if f
    s=getfile f
    sl=lcase s
    a=SearchString sl
  else
    s=chr 0
    a=0
  endif
  '
  SendMessage hd, WM_SETTEXT, 0 , strptr s
  if a>0
    a-=1
  endif
  SendMessage hd,EM_SETSEL,a,a+len(wordlist)
  'SendMessage hd, EM_SCROLLCARET, 0, 0
  return a
  end function




  function LocateHighlightKeyword(sys hl,hd,le) as sys
  ====================================================
  sys a
  a=val GetSelTextLine hl
  if a>0
    a-=1
  endif
  SendMessage hd,EM_SETSEL,a,a+le
  SendMessage hd, EM_SCROLLCARET, 0, 0
  return a
  end function




  function PostLocationList(sys hd, string f)
  ===========================================
  string s,sl
  s=""
  if wordlist
    if f
      s=getfile f
      sl=lcase s
      s=KeywordPosList sl
    endif
  endif
  '
  SendMessage hd, WM_SETTEXT, 0 , *s
  '
  end function



  function GetTextFromWindow(sys h,*e) as string
  ==============================================
  sys a
  a=SendMessage h, WM_GETTEXTLENGTH,0,0
  function=nuls a
  e=SendMessage h, WM_GETTEXT,a+1 , strptr(function)
  function=left function,e
  end function

  sub DisplayHelp
  '==============
  '
  string s= "HOW TO USE:" cr cr +
  "In the top line, Enter keywords or phrases separated by 2 commas ,," cr +
  "then press Enter to execute search." cr cr +

  "Click on filename to get list of line numbers" cr cr +
  "Click on line number to scroll down to the line" cr cr +
  "Search groups can be edited in the left-most panel, and these" cr +
  "will be retained for future use in tools/FindEd.cfg." cr cr +

   "F3 actions a key search 

   F4 actions a  whole-word search" cr cr+

  "Each search-group is separated by a blank line. A search-group is" cr+
  "selected by clicking on the first line in the group" cr cr+

  "The keywords are not case-sensitive" cr cr+

  "Files can be edited. Press F9 or ctrl-s to save the file after editing." cr cr+
  ".o2bas Files can be run. Press F5 or ctrl-e to run the selected file." cr
  MessageBox 0,s,"Instructions",MB_ICONINFORMATION
  end sub

 

  indexbase 0
  sys w[16] 'child windows


  =================================================================
  function WndProc(sys hwnd, uMsg, wParam, lParam) as long callback
  =================================================================
  '
  static RECT rcClient
  static sys hfont[4] 
  static sys a,b,c,
    h,i,px,py,lx,ly,id,
    style,stylr,
    newfile,wfocus
  static sys ce=-1, pe=-1
  '
  static string s,f,cf,fnt,st,ff
  if wfocus=101 'FILE LIST
    a=0xffff and SendMessage w[1], EM_GETSEL, 0, 0 'loword
    if a<>ce
      ce=a
      f=GetSelTextLine w[1]
      if f<>cf
        PostLocationList w[2],f
        a=PostFileContent f,w[1],w[3]
        'ce=0xffff and SendMessage w[1], EM_GETSEL, 0, 0 'loword
        cf=f
      endif
    endif
  elseif wfocus=102 'KEYWORD LOCATION LIST
    a=0xffff and SendMessage w[2], EM_GETSEL, 0, 0 'loword
    if a<>pe
      pe=a
      a=LocateHighlightKeyword w[2],w[3],len wordlist
      '
      'display line number
      int je
      je=SendMessage w[3], EM_LINEFROMCHAR, a, 0 'IDENTIFY LINE
      SendMessage w[5],WM_SETTEXT, 0, str(je+1) 
    endif
  endif
 
  select uMsg
  
  case WM_COMMAND
  ===============

    select wParam
    '
    case 4001 'SEARCH (F3)
    ======================
    '
    wholeword=0
    gosub DoSearch
    '
    case 4004 'WORD SEARCH (F4)
    ===========================
    '
    wholeword=1
    gosub DoSearch
    '
    '
    case 4002 'Save File (F9)
    =========================
    '
    if len cf
      s=GetTextFromWindow w[3],a
      putfile cf,s
      SetWindowText hwnd, cf " saved"
    endif
    '
   case 4003 'Exec File (F5)
   =========================
    '
    if len cf
        scope
        string sd=nuls(256)
        GetCurrentDirectory 256,sd
        sd=rtrim sd
        string sp=dirpath(cf,1)
        if sp
          SetCurrentDirectory(sp)
        endif
        string f=trimpath(cf)
        if right(cf,6)=".o2bas"
          string sc
          sc=AppExePath()
          QuExec(sc+"co2.exe","",f)
          SetWindowText hwnd, cf " Exec"
        elseif right(cf,4)=".bat"
          DOS(f,0)
          SetWindowText hwnd, cf " Exec"
        endif
        SetCurrentDirectory(sd)
        end scope
    endif
    '
    end select 
    '
    if hiword(wparam)=EN_SETFOCUS
      wfocus=loword(wparam)
    endif
    return 0
 
 
  case WM_CREATE
  ==============

  ff=getfile cfgs
  '
  if not ff
    ff="'Default list"+cr+"*.txt"+cr+"*.inc"+cr+"*.h"
  endif
  '
  ' Create three child windows.
  style=WS_CHILD | WS_BORDER | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_NOHIDESEL
  stylr=style | ES_READONLY
  int idmenu
  idmenu=0
  py=10 : ly=480
  '
  for i = 0 to 3
    id=ID_FIRSTCHILD+i
    px=10+i*200 : lx=180
    if i=1 or i=2
      a=stylr 'read only
    else
      a=style
    endif
    w[i]=CreateWindowEx 0,"edit", NULL, a, px,py,lx,ly, hwnd, id, inst, NULL
    SendMessage w[i],0xc5,0,0 'EM_LIMITTEXT = &HC5 limit for XP.. 7fffffff (before: XP 7fff)

  next
  '
  '
  id=104 : i=4 : px=10 : lx=10 : py=10 : ly=10
  style=WS_CHILD | WS_BORDER | WS_VISIBLE
  stylr=style | ES_READONLY

  'for keywords:
  w[4]=CreateWindowEx 0,"edit", NULL, style, px,py,lx,ly, hwnd, id, inst, NULL

  'for line number
  w[5]=CreateWindowEx 0,"edit", NULL, stylr, 0,0,0,0, hwnd, id, inst, NULL

  'action buttons
  style=WS_CHILD | BS_TEXT | BS_PUSHBOX | WS_VISIBLE
  w[6]=CreateWindowEx(0,"button", "keyw",  style, 0,0,0,0, hwnd, 4001, inst, null)
  w[7]=CreateWindowEx(0,"button", "match", style, 0,0,0,0, hwnd, 4004, inst, null)
  w[8]=CreateWindowEx(0,"button", "save",  style, 0,0,0,0, hwnd, 4002, inst, null)
  w[9]=CreateWindowEx(0,"button", "exec",  style, 0,0,0,0, hwnd, 4003, inst, null)


  'SetFont(sys *hwnd,n,height,width,weight,string fontname) as sys
  'hFont=SetFont(w[0],9,12,8,400,"Lucida Console")
  hFont[1]=SetFont(w[0],9,14,10,400,"Lucida Console")

  'insert directory list
  '
  h=w[0]
  SendMessage h,WM_SETTEXT,0,*ff
  '
  sys ce,je,le
  '
  s=GetFilteredFileLists ff
  a=w[1]
  SendMessage a, WM_SETTEXT, le , *s
  '
  'GET TEXT LINE INDEXBASE 0
  '
  s=nuls 256
  b=*s
  *b=256 'put buffer length info into buffer string
  'je=SendMessage a, EM_GETFIRSTVISIBLELINE, 0, 0
  je=0
  ce=SendMessage a, EM_LINEINDEX, je, 0
  'le=SendMessage a, EM_LINELENGTH, ce, 0
  le=SendMessage a, EM_GETLINE, je, b
  f=left s,le
  ' 
  s=left getfile(f), 32000
  le=len s
  h=w[3]
  SendMessage h, WM_SETTEXT, le , *s
  'h=w[3]
  'SendMessage h, WM_SETFOCUS, 0,0
  

  return 0

  'case WM_LBUTTONDOWN

  'return 0

  'case WM_NOTIFY
  'case WM_HELP

  case WM_SIZE
  ============ 

  'Set the window order
  'https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos
  '
  'sys hw=-1 'HWND_TOPMOST 'hw must be sys as a window handle
  'SetWindowPos(hwnd,hw,0,0,0,0,0x3)

 
  GetClientRect(hwnd, @rcClient) 
 
  'Size and position the child window.  
  int a,i,v,vb
  a=rcClient.right>>1
  for i=0 to 9
    v=rcClient.right*.20
    if v>160
      v=160
    endif
    vb=v*i
    if i<4
      if i=2
        v*=.5
      elseif i=3
        vb-=v*.5
        v=rcClient.right-vb ' wide window on the right
      endif
      MoveWindow(w[i], vb, 30, v, rcClient.bottom-60, TRUE) 
    else 'i>=4
      if i=4
        'keyword entry
        MoveWindow(w[4], 0, 5, a-20, 20, TRUE)
      endif
      if i=5
        'line number display
        MoveWindow(w[5], a, 5,50, 20, TRUE)
        a+=60
      endif
      if i>5
        'action buttons
        MoveWindow(w[i], a, 5,45, 20, TRUE)
        a+=50
      endif
    endif
    '
    ShowWindow(w[i], SW_SHOW)
  next
  return 0 


  case WM_CLOSE
  ============= 

  h=w[0]
  ff=GetTextFromWindow h,a
  putfile cfgs,ff
  DestroyWindow(hwndMain) 

  case WM_DESTROY: 
 
    DeleteObject hfont[1]
    PostQuitMessage(0) 
    return 0

  end select
  '
  return DefWindowProc(hwnd, uMsg, wParam, lParam)
  
    subroutine DoSearch
    ===================
    st=GetTextFromWindow w[4],a
    wordlist=lcase st
    SendMessage w[0], EM_GETSEL, @b , @c
    ff=GetTextFromWindow (w[0],a)
    b++
    StartOfLine ff,b
    ff=ltrim mid ff,b
    s=GetFilteredFileLists ff
    SendMessage w[1], WM_SETTEXT, 0 , strptr s 'file list
    SendMessage w[1], EM_SETSEL, 0 , 0
    f=GetSelTextLine w[1]
    'if f<>cf
    PostLocationList w[2],f
    a=PostFileContent f,w[1],w[3]
    cf=f
    'endif
    ce=0
    end subroutine

  end function




  Function WinMain(sys inst, prevInst, zstring* cmdline , sys show) as long
  '========================================================================
                    '
  ; window handle
  
  dim a,b,c,hWnd as long
  dim wc as WndClass
  dim wm as MSG

  with wc                '
    .style=CS_HREDRAW or CS_VREDRAW
    .lpfnWndProc=&WndProc '#long#long#long#long
    .cbClsExtra=0
    .cbWndExtra=0    
    .hInstance=inst
    .hIcon=LoadIcon 0, IDI_APPLICATION
    .hCursor=LoadCursor 0,IDC_ARROW
    .hbrBackground=GetStockObject WHITE_BRUSH '
    .lpszMenuName=0
    .lpszClassName=@"Wins"
  end with


  hinstance=inst

  if not RegisterClass &wc
    MessageBox 0,`Registration failed`,`Problem`,MB_ICONERROR
    exit function
  endif 


  int style=WS_OVERLAPPEDWINDOW

  ' Create the main window. 

   hwndMain = CreateWindowEx( 
    0,                   ' no extended styles           
    "Wins",              ' class name                   
    "Find words in files (F1 help)",' window name  
    style,               '                
    CW_USEDEFAULT,       ' default horizontal position  
    CW_USEDEFAULT,       ' default vertical position    
    800,                 ' default width                
    400,                 ' default height               
    NULL,                ' no parent or owner window    
    NULL,                ' class menu used              
    hinstance,           ' instance handle              
    NULL                 ' no window creation data      
    )


  ' Show the window using the flag specified by the program 
  ' that started the application, and send the application 
  ' a WM_PAINT message. 

  hwnd=hwndMain
      
  if not hWnd
    MessageBox 0,`Unable to create window`,`problem`,MB_ICONERROR
    exit function
  endif
                         '
  ShowWindow hWnd,show
  UpdateWindow hWnd
  ' 
  '
  sys bRet
  '
  do while bRet := GetMessage (&wm, 0, 0, 0)
    if bRet == -1
      mbox "message loop error "
      exit while
    else
      if wm.message=WM_KEYDOWN
        select wm.wparam
        '
        case 0x1B 'Esc
          SendMessage hwnd,WM_DESTROY,0,0 
          continue do
        case 83 'S
          if GetAsyncKeyState(VK_CONTROL) and 0x8000
            SendMessage hWnd,WM_COMMAND, 4002, 0 'to save file
            continue do
          endif
        case 69 'E
          if GetAsyncKeyState(VK_CONTROL) and 0x8000
            SendMessage hWnd,WM_COMMAND, 4003, 0 'to run file
            continue do
          endif
        case VK_F9 'save file
          SendMessage hWnd,WM_COMMAND, 4002, 0 'to save file
          continue do
        case VK_F5 'exec file
          SendMessage hWnd,WM_COMMAND, 4003, 0 'to run file
          continue do
        case VK_F1
          DisplayHelp
          continue do
        case VK_F3 '
         SendMessage hWnd,WM_COMMAND, 4001, 0 'to execute search
        case VK_F4 '
         SendMessage hWnd,WM_COMMAND, 4004, 0 'to execute word search
        case 0x0D 'CR / Enter
          if wm.hwnd=w[4]
            SendMessage hWnd,WM_COMMAND, 4001, 0 'to execute search
            continue do
          endif
        end select
      endif
      TranslateMessage &wm
      DispatchMessage &wm
    endif
  wend
  '
  '
  function=wm.wparam

  end function



  WinMain inst,0,cmdline,SW_NORMAL
  end

