/* TC Quick Search Plus, jful 2007 by Jaco Kwak
   Expand quicksearch functionality in TC by useing the ignore list
   pass the path: (%P) -Upper Case- to the program.
use:
    run the program.
    type a few letters and observe how the selection in the listbox shrinks.
    hit Enter to quit the program and return to TC.
    to return to the normal situation, run the program again and hit enter
     on the empty selection.

pros: folders are filtered,as well as files
unsupported: branch view
limitations: can only filter one panel, rerun needed to clear the filter,
the filter is static, i.e. is doesn't include new files you copy into the folder
caveats: in branch view you will see all files from unmatched folders,
you will NOT see any files from folders that do match.


--- version 2:
 [*] if no ignore list was previously defined in the ini file of total commander
     a new one is made in the same directory the ini file is in, and not in the
     directory total commander is installed in.
 [+] allow only 1 instance of the program to be active.
 [+] escape to exit program, restoring the original ignore list
 [*] corrected the following error: the program turns ON the ignore list, but
      never turns it OFF (solution save status in ini file)
--- version 2.1:
 [*] The program would delete the last line of the ignore list when the ignore list
      did not end with a linefeed. The fix now forces an extra linefeed onto the list.
--- version 2.2
 [*] bugfix filenames starting with a . were never filtered
 [+] feature request: added save window position
 [+] regular expressions added - add * before your search string to use this feature -
       library used: pcre by Philip Hazel , Perl 5 compatible regular expressions
  -  don't refresh list if regEx gives an error
 [*] change keys Enter: send to background; Escape: exit without filtering
      Alt-F4: exit without filtering; Ctrl-Enter: exit with filtering
      escape still works in background (i.e. works also if TCqsp does not have the focus)
 [+] F1 launches helpfile. Helpfile is closed on exit.
 [+] F2 toggle regular expression help
 [+]
     [Configuration]
    Normal_Escape=1
--- version 2.3:
 [*] bug fixed: crash when cheatSheet.txt not found
 [*] fixed: when the ignorelist was off on program start, this status was stored
   in the ini file and could never be changed again.
 [+] deactivate files in the ignorelist when TCqsp is called with filtering off.
     The user defined files in the ignore list are listed in this case now.
 [+] detect write access to ignore list, exit with a warning message if we don't have access
--- version 2.4:
 [+] save window position is now optional, if not used the window is positioned in the
     active file panel in the same position as the original TC quicksearch window
 [*] changes in window style:
      right-click on/near the word search or alt-space: brings up the menu now.
      a left-click and drag cam be used to move the window
 [*] made input mixed case (reg.expr. uses case sensitive switches), type lowercase letters when using reg.expr.
 [+] self installer, installs hotkey CS-Q
 
*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pcre\pcre.h"

HANDLE x,tcwnd;
WIN32_FIND_DATA 	w;
HWND hwndEdit3;
HWND hwndEdit1=0;
int iPaintIconError=0;

char *ignorelist;
char *tempfile;
FILE *f,*f2;
BOOL b=FALSE;
char *d;
char *t,*t2;
char *p,*p2;
char s1[1000];
char inifile[1000];
char *inipath;  // TC's ini files are found here
char *TCQSPini;
char *TCQSPdir;
int i;
RECT myPos; 	// address of structure for window coordinates
int fl_exit_filtered;
int cheatSheet;
static RECT rtF2;   // save window position on F2
HACCEL ha;          // accellerators..
ACCEL *aa,a[10];
int fl_escape_normal;   // 0=reacts always, 1=escape works only when TCqsp has the focus
int fl_winPos;          // 0=let TCqsp position window (default), 1=remember window position
int fl_helpLaunched;
HMENU hMenu;
int i_icon=1;
HWND hwnd;                  /* This is the handle for our window */
int originalFilterSetting;  // setting from wincmd.ini before it was altered by TCqsp
LONG qsPos_l,qsPos_t;       // 'would be' position of the TC quicksearch window (if it could be determined)



int regular_expression_match(char *, char *);
void RegExHelp(HWND hwnd);
int qsPos(void);                // find the position the original Quicksearch would be at
void paintIcon(int err,HANDLE hwnd);
void SelfInstall(void);
char *IniFind(char *section);   // find inifile even when redirected
void makeNewIni(void);          // if not exist make ini file


// clear old data from file
void wr0()  {
int i=0;
int cr=0;
f=fopen(ignorelist,"r");
f2=fopen(tempfile,"w");
while (f && !feof(f))   {
    *s1=0;
    fgets(s1,1000,f);
    if (f && f2 && *s1)   {
        if ( strstr(s1,"c:\\===--- TCQSP begin ===---") ) i=1;
        if ( strstr(s1,"c:\\===--- TCQSP end") ) i=2;
        if (i==0)   {
            if (cr==1)  {fputs("\n",f2);cr=0;}      // leave empty lines in tcignore.txt while..
            if (*s1!='\n')    {                     // bugfix for the case when the last character of tcignore.txt is not a \n EOL
                if (originalFilterSetting==0)   {   // don't filter the user-list if ignore was off
                    if (!strncmp(s1,"c:\\===--- TCQSP user list : ",28)) fputs(s1,f2);
                    else    fprintf(f2,"c:\\===--- TCQSP user list : %s",s1);
                    }
                else {  // remove string
                    if (strncmp(s1,"c:\\===--- TCQSP user list : ",28)) fputs(s1,f2);
                    else fputs(s1+28,f2);
                    }
                }
            else    cr=1;
            }
        if (i==2)   i=0;
        }
    }
if (f) fclose(f);
if (f2) fclose(f2);
unlink(ignorelist);
rename(tempfile,ignorelist);
}//wr0()

void wr(void)   {
char t0[1000];
if (*t!='*')    {
    strcpy(t0,t);
    strlwr(t0);
    }
wr0();
f=fopen(ignorelist,"a");
fprintf(f,"\nc:\\===--- TCQSP begin ===---\n");
fprintf(f,"c:\\===--- TCQSP debug [%s]\n",d);
fprintf(f,"c:\\===--- TCQSP debug '%s'\n",t);
fprintf(f,"c:\\===--- TCQSP debug '%s'\n",inifile);
sprintf(s1,"%s*.*",d);
x=FindFirstFile(s1,&w);
if (x==INVALID_HANDLE_VALUE)	b=FALSE;
else b=TRUE;
while(b)  {
    p=w.cFileName;p2=s1;
    if ( !(strcmp(p,".")==0 || strcmp(p,"..")==0) )    {
        while (*p)  {*p2=tolower(*p);p++;p2++;*p2=0;}
        if (*t=='*')   {   // * signifies regular expression
            if (0==regular_expression_match(t+1,s1))  fprintf(f,"%s%s\n",d,w.cFileName);
            } else  {
            if (!strstr(s1,t0))  fprintf(f,"%s%s\n",d,w.cFileName);
            }
        }
    b=FindNextFile(x,&w);
    }
FindClose(x);
fprintf(f,"c:\\===--- TCQSP end ===---\n");
fclose(f);
}//wr()


int regular_expression_match(char *expr, char *str){
int i;
int a[100];
pcre *pp;
char *er;
int e1;
int opts=0;
pp=pcre_compile(expr,opts,(const char **) &er,&e1,0);
if (pp) {
    i=pcre_exec(pp,0,str,strlen(str),0,opts,a,100);
    if (i>=0)  return 1;    // match
    else return 0;          // no match
    }
else {
    if (i_icon==1) paintIcon(1,0);//error icon
    i_icon=2;
    return -1; //error
    }
}


/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "TCQSPApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nFunsterStil)

{
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */
char s0[1000];
char s2[1000];


tcwnd=FindWindow(szClassName, NULL);
if (tcwnd)  {                   // allow only 1 instance
    SetForegroundWindow(tcwnd);
    return 1;
    }


    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (hThisInstance,"A");
    wincl.hIconSm = LoadIcon (hThisInstance,"A");
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default color as the background of the window */
    wincl.hbrBackground = (HBRUSH) (COLOR_BTNFACE+1);

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "TC Quick Search+",  /* Title Text */
           WS_DLGFRAME|WS_POPUP,            /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           280,                 /* The programs width */
           32,                  /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );


// determine TC instance to use
tcwnd=GetForegroundWindow();    // use foreground window if this is a tc instance
GetClassName(tcwnd,s1,999);     // else try to find a tc
if (strcmp(s1,"TTOTAL_CMD"))    tcwnd = FindWindow ("TTOTAL_CMD", NULL);
if (!tcwnd) return 1;

// check startup argument
d=lpszArgument;
if (*d==0)  {
    MessageBox(hwnd,"Use: TCqsp.exe <path>","Error",MB_OK);
    return 0;
    }
if (!strcmp(d,"SelfInstall"))  {
    SelfInstall();
    return 0;
    }

if (d[strlen(d)-1]!='\\') {//add trailing \ if not present
    sprintf(s1,"%s\\",d);
    d=strdup(s1);
    }

// get program path, location TCQSPini file
i=strlen(lpszArgument);
sprintf(s0,"%s",GetCommandLine());
s0[strlen(s0)-i-1]=0;
t=strrchr(s0,'\\');
 t[1]=0;TCQSPdir=strdup(s0 + (*s0=='\"' ?1:0 ));
sprintf(t+1,"tcqsp.ini");
t=s0;if (*t=='\"')  t++;
TCQSPini=strdup(t);
makeNewIni();
//MessageBox(hwnd,TCQSPini,"error",MB_OK);

// get location ini file
ExpandEnvironmentStrings("%COMMANDER_INI%",inifile,999);
inipath=strdup(inifile);    // put new ini file in this location if a ignore
t=strrchr(inipath,'\\');    // list does not exist already
t[1]=0;                     // path including trailing backslash

// get ini file even when redirected
GetPrivateProfileString("Configuration","RedirectSection","0",s1,999,inifile);
{char s[1000];ExpandEnvironmentStrings(s1,s,999);strcpy(s1,s);}
if (0==sscanf(s1,"%d",&i))  {       // ini filename found
    i=2;
    strcpy(inifile,s1);
    }
if (i==1)   {                       // use file in AlternateUserIni
    GetPrivateProfileString("Configuration","AlternateUserIni",inifile,s1,999,inifile);
    {char s[1000];ExpandEnvironmentStrings(s1,s,999);strcpy(s1,s);}
    strcpy(inifile,s1);
    }
if (i>0)    {       // add path to redirected ini file
    strcpy(s1,inifile);
    t=s2;*t=0;
    if (*s1=='\\' || s1[1]==':')    *t=1;    // path like c:\... or \\fs01\share\...
    if (*t==0)   {   // add path , no path , or filespec like ini\tcignore.txt
        sprintf(inifile,"%s%s",inipath,s1);
        }
    }

// read settings
i=GetPrivateProfileInt("Configuration","IgnoreListFileEnabled",0,inifile);
if (i==0)   {
    SendMessage (tcwnd, 1075, (WPARAM)2922, (LPARAM)0);// cm_SwitchIgnoreList
    Sleep(50);
    }
originalFilterSetting=GetPrivateProfileInt("Original_setting","IgnoreListFileEnabled",-1,TCQSPini);
if (-1 == originalFilterSetting )    {
    // store only if a previous setting was not found.
    // (a 0 or 1 setting means TCqsp was run and exited with filtering left on the last time)
    WritePrivateProfileString("Original_setting","IgnoreListFileEnabled",i?"1":"0",TCQSPini);
    originalFilterSetting=i;
    }

//{char s[1000];DWORD d;d=GetLastError();sprintf(s,"%lu '%s'",d,TCQSPini);MessageBox(hwnd,s,"error",MB_OK);}

GetPrivateProfileString("Configuration","IgnoreListFile","",s1,999,inifile);
if (*s1==0) {   // no list defined in inifile
    sprintf(s1,"%stcignore.txt",inipath);
    WritePrivateProfileString("Configuration","IgnoreListFile",s1,inifile);
    }
{char s[1000];ExpandEnvironmentStrings(s1,s,999);strcpy(s1,s);}
t=s2;*t=0;
if (*s1=='\\' || s1[1]==':')    *t=1;    // path like c:\... or \\fs01\share\...
if (*t==0)   {   // add path , no path , or filespec like ini\tcignore.txt
    sprintf(s0,"%s%s",inipath,s1);
    strcpy(s1,s0);
    }
ignorelist=strdup(s1);
t=strrchr(s1,'\\');
strcpy(t+1,"TCQSP.tmp");
tempfile=strdup(s1);

                        // test write access for modifying the ignore list
f2=fopen(ignorelist,"a");
if (!f2) {
    char s[1000];
    sprintf(s,"TCqsp needs write access to the ignorelist: '%s'\nPlease check your Total Commander settings and make sure you have full access privileges to the directory that the ini-files are in.",ignorelist);
    MessageBox(hwnd,s,"Write Error",MB_OK);
    return 0;
    }
else fclose(f2);
f2=fopen(tempfile,"w");
if (!f2) {
    MessageBox(hwnd,"TCqsp needs write access to the ignorelist directory.","Write Error",MB_OK);
    return 0;
    }
else fclose(f2);


// position the window on the screen
{RECT rt;
GetClientRect(tcwnd,&rt);
MoveWindow(hwnd,rt.left+(rt.right-rt.left)/2,rt.bottom-32,280,30,TRUE);
}


wr0();//remove old list of igored files and folders

{               // restore position of window between sesions
LONG w,h;
RECT r;
GetWindowRect(hwnd,&myPos);
w=myPos.right-myPos.left;   //width of window
h=myPos.bottom-myPos.top;
myPos.left=GetPrivateProfileInt("View","PosX",myPos.left,TCQSPini);
myPos.top=GetPrivateProfileInt("View","PosY",myPos.top,TCQSPini);
if (myPos.left>=0)   fl_winPos=1;
else    {                               // if ini file PosX=-1 then calculate position
    if (qsPos())    {//.....................
        myPos.left=qsPos_l;
        myPos.top=qsPos_t;
        }
    }
                // check and correct values with current screen-size
SystemParametersInfo(SPI_GETWORKAREA,0,&r,0);
if (myPos.left<r.left) myPos.left=r.left;
if (myPos.top<r.top) myPos.top=r.top;
if (myPos.left+w>r.right) myPos.left=r.right-w;
if (myPos.top+h>r.bottom) myPos.top=r.bottom-h;

SetWindowPos(hwnd,HWND_TOP,myPos.left,myPos.top, 0,0,SWP_NOSIZE);
}




fl_escape_normal=GetPrivateProfileInt("Configuration","Normal_Escape",0,TCQSPini);

hMenu = CreatePopupMenu();
AppendMenu (hMenu, MF_STRING,1001+WM_USER,"&About...") ;
AppendMenu (hMenu, MF_SEPARATOR,0,NULL) ;
AppendMenu (hMenu, MF_STRING,1002+WM_USER,"normal &Escape") ;
AppendMenu (hMenu, MF_STRING,1003+WM_USER,"remember window &Position") ;
CheckMenuItem(hMenu,1002+WM_USER,fl_escape_normal?MF_CHECKED:MF_UNCHECKED);
CheckMenuItem(hMenu,1003+WM_USER,fl_winPos?MF_CHECKED:MF_UNCHECKED);


    /* Make the window visible on the screen */
    ShowWindow (hwnd, nFunsterStil);

    // init search-string and start timer
    t=s0;t2=s2;
    *s1=0;
    *s2='*';s2[1]=0;//initial compare s1,s2
    SetTimer(hwnd,1,100,0);


// define accellerator keys
aa=a;
aa->fVirt=FVIRTKEY;
aa->key=VK_RETURN;
aa->cmd=(WM_USER+1);
aa++;
aa->fVirt=FVIRTKEY;
aa->key=VK_ESCAPE;
aa->cmd=(WM_USER+2);
aa++;
aa->fVirt=FVIRTKEY | FCONTROL;
aa->key=VK_RETURN;
aa->cmd=(WM_USER+3);
aa++;
aa->fVirt=FVIRTKEY;
aa->key=VK_F1;
aa->cmd=(WM_USER+4);
aa++;
aa->fVirt=FVIRTKEY;
aa->key=VK_F2;
aa->cmd=(WM_USER+5);
aa++;
aa->fVirt=FVIRTKEY | FALT;
aa->key=VK_SPACE;
aa->cmd=(WM_USER+6);

ha=CreateAcceleratorTable(a,6);





    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
       if (!TranslateAccelerator (hwnd, ha, &messages))
            {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
            }
    }

// cleanup
KillTimer(hwnd,1);
DestroyMenu(hMenu);
                // save position of window between sesions
if (cheatSheet) {
    myPos.left=rtF2.left;
    myPos.top=rtF2.top;
    }
else GetWindowRect(hwnd,&myPos);

if (fl_winPos==1)   {       // remember window pos..
    sprintf(s1,"%ld",myPos.left);WritePrivateProfileString("View","posX",s1,TCQSPini);
    sprintf(s1,"%ld",myPos.top);WritePrivateProfileString("View","posY",s1,TCQSPini);
    }
else WritePrivateProfileString("View","posX","-1",TCQSPini);    // ..or not

if (fl_escape_normal!=GetPrivateProfileInt("Configuration","Normal_Escape",0,TCQSPini))
    WritePrivateProfileString("Configuration","Normal_Escape",fl_escape_normal?"1":"0",TCQSPini);


if (!fl_exit_filtered)   {
    *t=0;
    if (strcmp(t,t2)!=0)  {     // changed?
        strcpy(t2,t);
        if ( (*t=='*' && regular_expression_match(t+1,"test")>-1)
             || *t!='*')    {   // don't refresh if regEx returns an error (like in an incomplete expression)
            wr();       // update...
            SendMessage (tcwnd, 1075, (WPARAM)2922, (LPARAM)0);// cm_SwitchIgnoreList
            Sleep(50);
            SendMessage (tcwnd, 1075, (WPARAM)2922, (LPARAM)0);// cm_SwitchIgnoreList
            Sleep(50);
            }
        }
    i=GetPrivateProfileInt("Original_setting","IgnoreListFileEnabled",-1,TCQSPini);
    if (i==0)   {
        SendMessage (tcwnd, 1075, (WPARAM)2922, (LPARAM)0);// cm_SwitchIgnoreList
        Sleep(50);
        WritePrivateProfileString("Original_setting","IgnoreListFileEnabled","0",TCQSPini);
        }
    WritePrivateProfileString("Original_setting","IgnoreListFileEnabled","-1",TCQSPini);
                        // remove junk from tcignore.txt
    if (originalFilterSetting==0)   {
        originalFilterSetting=1;
        wr0();
        originalFilterSetting=0;
        }
    }//if !fl_exit_filtered
    
    
if (fl_exit_filtered)   {
    if (*t=='\r'|| *t==0)   {   // restore original setting, if ignore list was not enabled from the start
        wr0();  // nothing filtered => remove junk
        i=GetPrivateProfileInt("Original_setting","IgnoreListFileEnabled",-1,TCQSPini);
        if (i==0)   {
            SendMessage (tcwnd, 1075, (WPARAM)2922, (LPARAM)0);// cm_SwitchIgnoreList
            Sleep(50);
            WritePrivateProfileString("Original_setting","IgnoreListFileEnabled","0",TCQSPini);
            }
        }
    }//if fl_exit_filtered


if (fl_helpLaunched) {   // close helpfile on exit
    HWND hHelp;
    hHelp=FindWindow("HH Parent","TC Quick Search Plus");
    if (hHelp)  PostMessage(hHelp,WM_QUIT,0,0);
    }

if (ha) DestroyAcceleratorTable(ha);


    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}//WinMain()


void DrawBitmap (HDC hdc, int xStart, int yStart, HBITMAP hBitmap)
     {
     BITMAP bm ;
     HDC    hMemDC ;
     POINT  pt ;

     hMemDC = CreateCompatibleDC (hdc) ;
     SelectObject (hMemDC, hBitmap) ;
     GetObject (hBitmap, sizeof (BITMAP), (PSTR) &bm) ;
     pt.x = bm.bmWidth ;
     pt.y = bm.bmHeight ;

     BitBlt (hdc, xStart, yStart, pt.x, pt.y, hMemDC, 0, 0, SRCCOPY) ;

     DeleteDC (hMemDC) ;
     }


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT r;
static int mmm=0;
    switch (message)                  /* handle the messages */
    {

        case WM_CREATE:
                    hwndEdit3 = CreateWindowEx (
                        0,
                        "edit",    /* Builtin edit class */
                        NULL,
                         WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | //WS_BORDER|
                             ES_LEFT ,//| ES_LOWERCASE ,
                        0, 0, 200, 20,
                        hwnd,        /* Parent is this window. */
                        (HMENU) 3,    /* Control ID: 3 */
                        (HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE),
                        NULL
                        );
                    SendMessage(hwndEdit3,EM_SETLIMITTEXT,99,0);
                    SetFocus(hwndEdit3);

            return 0;
            break;

        case WM_PAINT:
            {            /* The window needs to be painted (redrawn). */
            HDC hdc;
            PAINTSTRUCT ps;
            RECT rc;
            HBITMAP hBitmap;
            static COLORREF cc=CLR_INVALID;
            hdc = BeginPaint (hwnd, &ps);
            GetClientRect (hwnd, &rc);
            rc.left+=24;
            rc.top+=4;
//            if (cc==CLR_INVALID)    cc=GetPixel(hdc,1,1);
//            if (cc==CLR_INVALID)    cc=0x808080;
            cc=GetSysColor(COLOR_BTNFACE);
            SetBkColor(hdc,cc);
            DrawText (hdc, "Search:", -1, &rc,0);
            hBitmap = LoadBitmap ((HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE),iPaintIconError?"BMP4":"BMP3") ;
            DrawBitmap (hdc, 4, 4, hBitmap) ;
            DeleteObject (hBitmap) ;
            EndPaint (hwnd, &ps);
            return 0;
            break;
            }
        case WM_SIZE :
            if (cheatSheet)  {
                MoveWindow(hwndEdit3, 78, 2,LOWORD(lParam)-80,20, TRUE);
                MoveWindow(hwndEdit1, 0, 24,LOWORD(lParam),HIWORD(lParam)-24, TRUE);
                } else {
                MoveWindow(hwndEdit3, 78, 2,LOWORD(lParam)-80,20, TRUE);
                }
            return 0;
            break;
        case WM_TIMER:
            KillTimer(hwnd,1);
            *t=0;
            i=SendMessage(hwndEdit3,WM_GETTEXT,(WPARAM) 99,(LPARAM) t);
            t[i]=0;
            if (fl_escape_normal==0 && GetKeyState(VK_ESCAPE)<0) {// escape = quit, restore ignore list
                *t=0;
                PostQuitMessage (0);
                }
            if (strcmp(t,t2)!=0)  {     // changed?
                strcpy(t2,t);
                if ( (*t=='*' && regular_expression_match(t+1,"test")>-1)
                     || *t!='*')    {   // don't refresh if regEx returns an error (like in an incomplete expression)
                    if (i_icon==2) paintIcon(0,0);//ok icon
                    i_icon=1;
                    wr();       // update...
                    SendMessage (tcwnd, 1075, (WPARAM)2922, (LPARAM)0);// cm_SwitchIgnoreList
                    Sleep(50);
                    SendMessage (tcwnd, 1075, (WPARAM)2922, (LPARAM)0);// cm_SwitchIgnoreList
                    Sleep(50);
                    }
                }

            SetTimer(hwnd,1,100,0);
            return 0;
            break;

        case WM_ACTIVATE:
            SetFocus(hwndEdit3);
            return 0;
            break;

        case WM_MOUSEMOVE :
            if (mmm)    {
                int x,y;
                short int xx,yy;
                static short int x1=0,y1=0;
                xx = LOWORD (lParam) ;
                yy = HIWORD (lParam) ;
                if (x1==0)    x1=xx;
                if (y1==0)    y1=yy;
                x=xx-x1;
                y=yy-y1;
                GetWindowRect(hwnd,&r);
                MoveWindow(hwnd, r.left+x,r.top+y,r.right-r.left,r.bottom-r.top,TRUE);
                x1=xx-x;
                y1=yy-y;
                }//if mmm
            return 0;

        case WM_LBUTTONDOWN :
        case WM_LBUTTONUP :
            mmm=1-mmm;
            if (mmm)    SetCapture(hwnd);
            else    ReleaseCapture();
            return 0 ;

        case WM_RBUTTONDOWN :
            {POINT        point ;
            point.x = LOWORD (lParam) ;
            point.y = HIWORD (lParam) ;
            ClientToScreen (hwnd, &point) ;
            TrackPopupMenu (hMenu, 0, point.x, point.y, 0, hwnd, NULL) ;
            return 0 ;}

                    
        case WM_COMMAND:
            if (HIWORD(wParam)==1)  {//accelerator keys
                int k;
                k=LOWORD(wParam)-WM_USER;
                switch (k)  {
                    case 1:                         // Enter
                        SetForegroundWindow(tcwnd); // send to background
                        break;
                    case 2:                         // Escape
                        if (fl_escape_normal==1)
                            PostQuitMessage (0);    // exit with filtering left on
                        break;
                    case 3:                         // ctrl-enter
                        PostQuitMessage (0);        // exit with filtering left on
                        fl_exit_filtered=1;
                        break;
                    case 4:                         // F1
                        sprintf(s1,"%sTCqsp.chm",TCQSPdir);
                        ShellExecute(hwnd,"open",s1,0,TCQSPdir,SW_SHOWDEFAULT);
                        fl_helpLaunched=1;
                        break;
                    case 5:                         // F2
                        if (cheatSheet) {
                            cheatSheet=0;
                            ShowWindow(hwndEdit1,SW_HIDE);
                            MoveWindow(hwnd,rtF2.left,rtF2.top,rtF2.right-rtF2.left,rtF2.bottom-rtF2.top, TRUE);
                            SetFocus(hwndEdit3);
                            } else {
                            GetWindowRect(hwnd,&rtF2);
                            RegExHelp(hwnd);
                            ShowWindow(hwndEdit1,SW_SHOW);
                            MoveWindow(hwnd,100,100,700,500, TRUE);
                            }
                        break;
                    case 6:                         // Alt-space  (popup menu)
                        GetWindowRect(hwnd,&r);
                        TrackPopupMenu (hMenu, 0, r.left, r.top, 0, hwnd, NULL) ;
                        break;
                    }
                }// accel keys
                //HIWORD(wParam) == STN_CLICKED
            switch (LOWORD(wParam)-WM_USER) {
            case 1001:
                MessageBox (hwnd, "TC Quick Search +\nversion 2.4 Okt 2007","About", MB_OK | MB_ICONINFORMATION) ;
                return 0 ;
            case 1002:
                fl_escape_normal=1-fl_escape_normal;
                CheckMenuItem(hMenu,1002+WM_USER,fl_escape_normal?MF_CHECKED:MF_UNCHECKED);
                return 0 ;
            case 1003:
                fl_winPos=1-fl_winPos;
                CheckMenuItem(hMenu,1003+WM_USER,fl_winPos?MF_CHECKED:MF_UNCHECKED);
                return 0 ;
                }
            break;

        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}




void RegExHelp(HWND hwnd)  {
static char *s=0;
FILE *f=0;
if (!s) {
    sprintf(s1,"%sCheatSheet.txt",TCQSPdir);
    f=fopen(s1,"rb");
    if (f)  {
        char a[10000];
        *a=0;
        fread(a,1,10000,f);
        fclose(f);
        if (*a)  s=strdup(a);
        }
    }
if (!s)  s="Error: reading the file CheatSheet.txt";

if (!hwndEdit1) {
    hwndEdit1 = CreateWindow (
        "Edit",    /* Builtin edit class */
        NULL,
         WS_CHILD | WS_VISIBLE | WS_VSCROLL|ES_AUTOVSCROLL|
              WS_BORDER | ES_LEFT | ES_MULTILINE ,
        0, 40, 200, 40,
        hwnd,        /* Parent is this window. */
        (HMENU) 1,    /* Control ID */
        /*((LPCREATESTRUCT) lParam)->hInstance,*/
        (HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE),
        NULL
        );

    SendMessage(hwndEdit1,WM_SETTEXT,0,(LPARAM) s);
    SendMessage(hwndEdit1,EM_SETREADONLY,(WPARAM) (BOOL) TRUE,0);
    }
SetFocus(hwndEdit1);
cheatSheet=1;
}//RegExHelp()



int qsPos(void) {   // find the position the original Quicksearch would be at
// works as long as the directory (address bar?) above the file list is not obscured
//char s[1000],s0[1000];
DWORD d;
HDC xx;COLORREF cc;RECT rr;
HWND h1,h2,h3;
int found=0;
int errorcount=0;
qsPos_l=0;
qsPos_t=0;
d=GetSysColor(COLOR_ACTIVECAPTION);     // get the color of the control above the active filelist panel

h1=NULL;
while (h1=FindWindowEx(tcwnd,h1,"TMyPanel",NULL))   {
    h2=NULL;
    while (h2=FindWindowEx(h1,h2,"TMyTabControl",NULL))   {
        h3=NULL;
        while (h3=FindWindowEx(h2,h3,"TMyPanel",NULL))   {
            // possibly use text later to find the dir
            //GetWindowText(h3,s0,999);
                if (!found) {
                    qsPos_l=rr.left;
                    qsPos_t=rr.top;
                    }
                if (0==GetWindowRect(h3,&rr)) errorcount++;
                xx=GetDC(h3);
                if (!xx) errorcount++;
                cc=GetPixel(xx,1,1);//if (cc==CLR_INVALID) errorcount++; // not counting these errors allows you to shove part of TC offscreen, of obscure it by another always on top program
                ReleaseDC(h3,xx);
                //sprintf(s,"%s  \n%lx \n%ld,%ld,%ld,%ld",s0,cc,rr.left,rr.right,rr.top,rr.bottom);
                if (d==cc && errorcount==0 && qsPos_l!=0 && qsPos_t!=0)  {
                //    MoveWindow (hwnd, qsPos_l,qsPos_t,280,30, TRUE) ;    // test pos
                //    MessageBox(hwnd,s,"color? xy?",MB_OK);
                    found=1;
                        // use previous rr.left,rr.top for position (this is the one within the same TMyTabControl)
                    }
            }//while..TMypanel
        }//while..Tab
        
    h3=NULL;                                                // when no-tabs show..
    while (h3=FindWindowEx(h1,h3,"TMyPanel",NULL))   {
        // possibly use text later to find the dir
        //GetWindowText(h3,s0,999);
            if (!found) {
                qsPos_l=rr.left;
                qsPos_t=rr.top;
                }
            if (0==GetWindowRect(h3,&rr)) errorcount++;
            xx=GetDC(h3);
            if (!xx) errorcount++;
            cc=GetPixel(xx,1,1);//if (cc==CLR_INVALID) errorcount++; // not counting these errors allows you to shove part of TC offscreen, of obscure it by another always on top program
            ReleaseDC(h3,xx);
            //sprintf(s,"%s  \n%lx \n%ld,%ld,%ld,%ld",s0,cc,rr.left,rr.right,rr.top,rr.bottom);
            if (d==cc && errorcount==0 && qsPos_l!=0 && qsPos_t!=0)  {
            //    MoveWindow (hwnd, qsPos_l,qsPos_t,280,30, TRUE) ;    // test pos
            //    MessageBox(hwnd,s,"color? xy?",MB_OK);
                found=1;
                    // use previous rr.left,rr.top for position (this is the one within the same TMyTabControl)
                }
        }//while..TMypanel

        
    }//while..panel

return found;   // positions in global variables: qsPos_l qsPos_t

}//qsPos()


void paintIcon(int err,HANDLE hwnd) {   // 'icon' red when reg.expr. error
iPaintIconError=err;
InvalidateRect(hwnd,NULL,TRUE);
};//paintIcon()


char *IniFind(char *section)	{// find inifile even when redirected
static char inifile[1000];
char *inipath,*t;
char s1[1000],s2[1000];
// get location ini file
ExpandEnvironmentStrings("%COMMANDER_INI%",inifile,999);
inipath=strdup(inifile);    // put new ini file in this location if a ignore
t=strrchr(inipath,'\\');    // list does not exist already
t[1]=0;                     // path including trailing backslash

// get ini file even when redirected
GetPrivateProfileString(section,"RedirectSection","0",s1,999,inifile);
{char s[1000];ExpandEnvironmentStrings(s1,s,999);strcpy(s1,s);}
if (0==sscanf(s1,"%d",&i))  {       // ini filename found
    i=2;
    strcpy(inifile,s1);
    }
if (i==1)   {                       // use file in AlternateUserIni
    GetPrivateProfileString(section,"AlternateUserIni",inifile,s1,999,inifile);
    {char s[1000];ExpandEnvironmentStrings(s1,s,999);strcpy(s1,s);}
    strcpy(inifile,s1);
    }
if (i>0)    {       // add path to redirected ini file
    strcpy(s1,inifile);
    t=s2;*t=0;
    if (*s1=='\\' || s1[1]==':')    *t=1;    // path like c:\... or \\fs01\share\...
    if (*t==0)   {   // add path , no path , or filespec like ini\tcignore.txt
        sprintf(inifile,"%s%s",inipath,s1);
        }
    }
return inifile;
}//IniFind()

int fexist(char *s)    {//file exists
HANDLE h;
WIN32_FIND_DATA ffd;
int r;
h= FindFirstFile(s,&ffd);
if (h==INVALID_HANDLE_VALUE) r=0;   // 0= nee
else r=1;                           // 1= ja
FindClose(h);
return r;
}//fexist



void SelfInstall(void)  {
char a[1000],b[1000];
char *p,*t;
int fl_SelfInstall=0;
//MessageBox(hwnd,p,"inifile",MB_OK);

        // part 1 : make usercommand entry
p=IniFind("Configuration");
t=strrchr(p,'\\');t[1]=0;
sprintf(a,"%susercmd.ini",p);
if (!fexist(a)) {FILE *f;f=fopen(a,"w");if (f) fclose(f);}
GetPrivateProfileString("em_startTCQSP","cmd","",s1,999,a);
if (!*s1) {   // not installed, so install now
    WritePrivateProfileString("em_startTCQSP","cmd","%COMMANDER_PATH%\\TCQSP\\TCQSP.exe",a);
    WritePrivateProfileString("em_startTCQSP","param","%P",a);
    //MessageBox(hwnd,a,"em..installed",MB_OK);
    fl_SelfInstall=1;
    }

    // part 2 : make shortcut entry
p=IniFind("Shortcuts");
GetPrivateProfileString("Shortcuts","CS+Q","",s1,999,p);//.... redir...
if (!*s1)   {
    WritePrivateProfileString("Shortcuts","CS+Q","em_startTCQSP",p);
    //MessageBox(hwnd,p,"shortcut installed",MB_OK);
    fl_SelfInstall+=2;
    }

switch (fl_SelfInstall) {
    case 0:
        MessageBox(hwnd,"No Changes were made to your Total Commander configuration.\n","Program already installed",MB_OK);
        break;
    case 1:
        MessageBox(hwnd,"Hotkey setup was unsuccessfull. Setup hotkeys manually as instructed in the help file.","Program installation",MB_OK);
        break;
    case 2:
    case 3:
        MessageBox(hwnd,"Changes were made to your Total Commander configuration.\nAfter you have restarted TC you can bring up TCqsp with the hotkey Ctrl+Shift+Q.","Program installed",MB_OK);
        break;
    }

}//SelfInstall()

void makeNewIni(void)   { // if not exist make ini file
if (!fexist(TCQSPini)) {
    FILE *f;
    f=fopen(TCQSPini,"w");
    if (f) {
        fputs("[Original_setting]\n",f);
        fputs("IgnoreListFileEnabled=-1\n",f);
        fputs("[View]\n",f);
        fputs("posX=-1\n",f);
        fputs("posY=-1\n",f);
        fputs("[Configuration]\n",f);
        fputs("; Normal_Escape=0 allows you to quit TCqsp while it is still in the background\n",f);
        fputs("; when Normal_Escape=1 TCqsp only reacts to the escape key when it is in the foreground\n",f);
        fputs("Normal_Escape=0\n",f);
        fclose(f);
        }
    }
}//makeNewIni()
