/*
 *      Copyright (C) 1997-2001 Andrei Los.
 *      This file is part of the lSwitcher source package.
 *      lSwitcher is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the lSwitcher main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

#include <process.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "common.h"
#include "msg.h"

#ifdef XWORKPLACE
  #include "dlgids.h"
#endif

ULONG MapCommand(USHORT cmd)
{
  switch (cmd) {
  case CMD_HIDE:     return MAKEULONG(SC_HIDE,SWP_HIDE);
  case CMD_MAXIMIZE: return MAKEULONG(SC_MAXIMIZE,SWP_MAXIMIZE);
  case CMD_MINIMIZE: return MAKEULONG(SC_MINIMIZE,SWP_MINIMIZE);
  case CMD_RESTORE:  return MAKEULONG(SC_RESTORE,SWP_RESTORE);
  case CMD_SHOW:     return MAKEULONG(0,SWP_SHOW);
  case CMD_MOVE:     return MAKEULONG(SC_MOVE,SWP_MOVE);
  case CMD_CLOSE:    return MAKEULONG(SC_CLOSE,0);
  default: return 0;
  }
}

/* This advanced killing Copyright (C)1996 by Holger.Veit@gmd.de */
HFILE OpenXF86(VOID)
{ HFILE hfd;
  ULONG action;

  if (DosOpen((PSZ)"/dev/fastio$", (PHFILE)&hfd, &action, 0, FILE_SYSTEM, FILE_OPEN,
      OPEN_SHARE_DENYNONE|OPEN_FLAGS_NOINHERIT|OPEN_ACCESS_READONLY, 0)!=0)
    return 0;
  else
    return hfd;
}

BOOL Death(PID pid)
{ HFILE hfd;
  ULONG plen;
  USHORT param;


  if ((hfd=OpenXF86())==0) return FALSE;
  param = pid;

  if (DosDevIOCtl(hfd,0x76,0x65,(PULONG*)&param,sizeof(USHORT),&plen,NULL,0,NULL)!=0) {
    DosClose(hfd);
    return FALSE;
  }

  DosClose(hfd);
  return TRUE;
}

BOOL ChangeWindowPos(LSWDATA *plswData,SHORT iItemNum,USHORT cmd)
{ USHORT cmd1;
  BOOL bDoIt;

  cmd1=LOUSHORT(MapCommand(cmd));

  bDoIt = ((cmd==CMD_KILL || cmd==CMD_DEATH || cmd==CMD_CLOSE || cmd==CMD_CLOSEQUIT) ? TRUE :
           cmd == CMD_SHOW ? (!(plswData->TaskArr[iItemNum].fl & SWP_SHOW)):
           WndHasControl(plswData,plswData->TaskArr[iItemNum].hwnd,cmd1));

  if (bDoIt) {
    if (cmd==CMD_SHOW)
      WinShowWindow(plswData->TaskArr[iItemNum].hwnd,TRUE);
    else if (cmd==CMD_CLOSEQUIT)
      WinPostMsg(plswData->TaskArr[iItemNum].hwnd, WM_QUIT, 0,0);
    else if (cmd==CMD_KILL)
      DosKillProcess(DKP_PROCESS,plswData->TaskArr[iItemNum].pid);
    else if (cmd==CMD_DEATH)
      Death(plswData->TaskArr[iItemNum].pid);
    else
      WinPostMsg(plswData->TaskArr[iItemNum].hwnd,WM_SYSCOMMAND,MPFROMSHORT(cmd1),MPFROM2SHORT(CMDSRC_OTHER,FALSE));
  }

  return bDoIt;
}


/* this procedure returns TRUE if the the popup menu for the usItem item in
 the TaskArr needs the menu item usId. If yes, the title is returned also */
BOOL MenuNeedsItem(LSWDATA *plswData,USHORT usItem,USHORT usId,UCHAR *pszTitle,USHORT usLen)
{ BOOL bNeedsItem;

  if (usId == CMD_SWITCHFROMPM || usId == CMD_CLOSEQUIT)
    bNeedsItem = TRUE;
  else if (usId==CMD_KILL || usId==CMD_DEATH || usId==CMD_PRIORITY) {
    PSWBLOCK pSwb;
    ULONG ulItemCount,k;
    PID pidWPS;

    if ((pSwb=GetSwitchList(plswData->hab,TRUE,&ulItemCount))!=NULL) {
      for (k=0,pidWPS=0;k<ulItemCount;k++)
        if (strncmp(pSwb->aswentry[k].swctl.szSwtitle,"Workplace Shell",15)==0) {
          pidWPS=pSwb->aswentry[k].swctl.idProcess;
          break;
        }
      GetSwitchList(0,FALSE,NULL);
      bNeedsItem=(plswData->TaskArr[usItem].pid!=pidWPS);
      if (usId==CMD_DEATH) {
        HFILE hfd;
        if ((hfd = OpenXF86())!=0) DosClose(hfd);
        bNeedsItem &=(hfd!=0);
      }
    } else
      bNeedsItem=FALSE;
  } else if (usId == CMD_ADDFILTER)
    bNeedsItem = TRUE;
  else if (usId == CMD_MOVE) {
    bNeedsItem =
      (!(plswData->TaskArr[usItem].fl & SWP_HIDE) &&
       !IsMinToViewer(plswData->TaskArr[usItem].hwnd,plswData->TaskArr[usItem].fl) &&
       WndHasControl(plswData,plswData->TaskArr[usItem].hwnd,SC_MOVE));
  } else if (usId == CMD_SHOW)
    bNeedsItem = (plswData->TaskArr[usItem].fl & SWP_HIDE);
  else
    bNeedsItem = WndHasControl(plswData,plswData->TaskArr[usItem].hwnd,LOUSHORT(MapCommand(usId)));

  if (usId == CMD_HIDE) bNeedsItem &=(!(plswData->TaskArr[usItem].fl & SWP_HIDE));

  if (bNeedsItem)
    WinLoadString(plswData->hab,plswData->hmodRes,usId,usLen,pszTitle);

  return bNeedsItem;
}

BOOL WndHasControl(LSWDATA *plswData,HWND hwndToCheck,USHORT usControl)
{ HWND hwndSysMenu, hwndMinMax;

  return
    (WinIsWindow(plswData->hab,hwndToCheck) && WinIsWindowEnabled(hwndToCheck) &&
     (
      (
       (hwndSysMenu=WinWindowFromID(hwndToCheck,FID_SYSMENU))!=NULLHANDLE &&
       SHORT1FROMMR(WinSendMsg(hwndSysMenu,MM_ISITEMVALID,MPFROM2SHORT(usControl,TRUE),0))==TRUE
      ) ||
      (
       (hwndMinMax=WinWindowFromID(hwndToCheck,FID_MINMAX))!=NULLHANDLE &&
       SHORT1FROMMR(WinSendMsg(hwndMinMax,MM_QUERYITEMCOUNT,0,0))>0 &&
       (SHORT)WinSendMsg(hwndMinMax,MM_ITEMPOSITIONFROMID,MPFROM2SHORT(usControl,0),0)!=MIT_NONE
      )
     )
    );
}

VOID ShowMenu(LSWDATA *plswData,SHORT iMenuAtItem,BOOL bTaskBar)
{ MENUITEM mi={0},mi1={0};
  POINTL ptl;
  SHORT sItemId,sItemNum,sLastId,k;
  UCHAR ucBuf[64];
  HWND hwndSubmenu;

  sItemNum = SHORT1FROMMR(WinSendMsg(plswData->hwndMenu,MM_QUERYITEMCOUNT,0,0));
  while (sItemNum > 0) {
    sItemId = SHORT1FROMMR(WinSendMsg(plswData->hwndMenu,MM_ITEMIDFROMPOSITION,
                                      MPFROMSHORT(sItemNum-1),0));
    sItemNum = SHORT1FROMMR(WinSendMsg(plswData->hwndMenu,MM_DELETEITEM,
                                       MPFROM2SHORT(sItemId,FALSE),0));
  }

  mi.iPosition = mi1.iPosition = MIT_END;
  mi.afStyle = MIS_TEXT;
  mi1.afStyle = MIS_SEPARATOR;
  mi.afAttribute = mi.hwndSubMenu = mi.hItem =
    mi1.afAttribute = mi1.hwndSubMenu = mi1.hItem = 0;

  if (iMenuAtItem >= 0) {
    for (mi.id = CMD_SWITCHFROMPM,sLastId=0; mi.id <= CMD_MOVE; mi.id++) {
      if (!bTaskBar && mi.id==CMD_MOVE) continue;
      if (MenuNeedsItem(plswData,iMenuAtItem,mi.id,ucBuf,sizeof(ucBuf))) {
        if (mi.id == CMD_CLOSE || mi.id == CMD_KILL || sLastId==CMD_SWITCHFROMPM)
          WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi1), MPFROMP(ucBuf));

        WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));
        sLastId=mi.id;
      }
    }

    mi.id = CMD_CLOSE;
    if (!IsDesktop(plswData->TaskArr[iMenuAtItem].hwnd)) {
      WinLoadString(plswData->hab,plswData->hmodRes,CMD_CLOSE,sizeof(ucBuf),ucBuf);
      WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi1), MPFROMP(ucBuf));
      hwndSubmenu = WinCreateMenu(plswData->hwndMenu, NULL);

      if (hwndSubmenu != NULLHANDLE) {
        WinSetWindowULong(hwndSubmenu,QWL_STYLE,
                          WinQueryWindowULong(hwndSubmenu,QWL_STYLE) | MS_CONDITIONALCASCADE);
        mi.afStyle = MIS_TEXT | MIS_SUBMENU;
        mi.hwndSubMenu = hwndSubmenu;
        sItemId = SHORT1FROMMR(WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi),MPFROMP(ucBuf)));

        if (sItemId != MIT_MEMERROR && sItemId != MIT_ERROR) {
          mi.hwndSubMenu = 0;  mi.afStyle = MIS_TEXT;  mi.afAttribute = MIA_CHECKED;
          if (MenuNeedsItem(plswData,iMenuAtItem,mi.id,ucBuf,sizeof(ucBuf))) {
            WinSendMsg(hwndSubmenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP("SC_CLOSE"));
            mi.afAttribute = 0;
          }
          mi.id = CMD_CLOSEQUIT;
          WinSendMsg(hwndSubmenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP("WM_QUIT"));

          mi.id = CMD_KILL; mi.afAttribute = 0;
          if (MenuNeedsItem(plswData,iMenuAtItem,mi.id,ucBuf,sizeof(ucBuf)))
            WinSendMsg(hwndSubmenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));

          mi.id = CMD_DEATH;
          if (MenuNeedsItem(plswData,iMenuAtItem,mi.id,ucBuf,sizeof(ucBuf)))
            WinSendMsg(hwndSubmenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));
        }
      }
    }

    mi.id = CMD_PRIORITY;
    if (MenuNeedsItem(plswData,iMenuAtItem,mi.id,ucBuf,sizeof(ucBuf))) {
      WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi1), MPFROMP(ucBuf));
      WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));
    }
    mi.id = CMD_ADDFILTER;
    if (MenuNeedsItem(plswData,iMenuAtItem,mi.id,ucBuf,sizeof(ucBuf))) {
      WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi1), MPFROMP(ucBuf));
      WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));
    }
  }
#ifndef XWORKPLACE
  else {
    mi.id = CMD_RUN;
    WinLoadString(plswData->hab,plswData->hmodRes,STRID_RUN,sizeof(ucBuf),ucBuf);
    WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));
    mi.id = CMD_SHOWSETTINGS;
    WinLoadString(plswData->hab,plswData->hmodRes,STRID_SHOWSETTINGS,sizeof(ucBuf),ucBuf);
    WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));
    mi.id = CMD_QUIT;
    WinLoadString(plswData->hab,plswData->hmodRes,STRID_QUIT,sizeof(ucBuf),ucBuf);
    WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));
  }
#endif

#ifdef XWORKPLACE
// now copy the old context menu as submenu;
// this code adapted from XCenter winlist widget
  if (bTaskBar) {
    if (iMenuAtItem >= 0) {
      WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi1), MPFROMP(ucBuf));

      hwndSubmenu = WinCreateMenu(plswData->hwndMenu, NULL);

//      WinSetWindowUShort(hwndSubmenu,QWS_ID,CMD_XCENTERSUBMENU);

      mi.afAttribute = mi.hItem = 0;
      mi.afStyle = MIS_TEXT | MIS_SUBMENU;
      mi.id = CMD_XCENTERSUBMENU;
      mi.hwndSubMenu = hwndSubmenu;

      WinLoadString(plswData->hab,plswData->hmodRes,STRID_XCENTERSUBMENU,sizeof(ucBuf),ucBuf);

      sItemId = SHORT1FROMMR(WinSendMsg(plswData->hwndMenu, MM_INSERTITEM, MPFROMP(&mi),MPFROMP(ucBuf)));
    } else
      sItemId = 0;

    if (sItemId != MIT_MEMERROR && sItemId != MIT_ERROR) {
      sItemNum = SHORT1FROMMR(WinSendMsg(plswData->pWidget->hwndContextMenu,
                                         MM_QUERYITEMCOUNT, 0, 0));

      // loop through all entries in the original menu
      for (k = 0; k < sItemNum; k++) {
        sItemId = SHORT1FROMMR(WinSendMsg(plswData->pWidget->hwndContextMenu,
                               MM_ITEMIDFROMPOSITION, MPFROMSHORT(k), 0));
      // get this menu item into mi buffer
        WinSendMsg(plswData->pWidget->hwndContextMenu, MM_QUERYITEM,
                   MPFROM2SHORT(sItemId, FALSE), MPFROMP(&mi));
        if (mi.id==ID_CRMI_PROPERTIES) mi.id = CMD_SHOWSETTINGS;
      // query text of this menu entry into our buffer
        WinSendMsg(plswData->pWidget->hwndContextMenu, MM_QUERYITEMTEXT,
                   MPFROM2SHORT(sItemId, sizeof(ucBuf)-1), MPFROMP(ucBuf));
      // add this entry to our new menu
        mi.iPosition = MIT_END;
        WinSendMsg(iMenuAtItem < 0 ? plswData->hwndMenu : hwndSubmenu,
                   MM_INSERTITEM, MPFROMP(&mi), MPFROMP(ucBuf));
      }
    }
  }
#endif

  WinQueryPointerPos(HWND_DESKTOP,&ptl);
  WinPopupMenu(HWND_DESKTOP, bTaskBar?plswData->hwndTaskBarClient:plswData->hwndPopClient, plswData->hwndMenu, ptl.x, ptl.y,
               0, PU_HCONSTRAIN | PU_VCONSTRAIN | PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 | PU_KEYBOARD);
}

VOID ShowBubble(LSWDATA *plswData,SHORT iMouseIsAtItem,USHORT usX,USHORT usY,SHORT iFunc)
{ static SHORT iMouseWasAtItem=-1,iCounter1=0,iBubbleAtItem=-1,iSource;
  USHORT usSizeX,usSizeY;
  POINTL aptl[TXTBOX_COUNT];
  UCHAR ucTitle[NAMELEN];
  HPS hps;
  LONG cxScreen;
  SWP swp;

  if (iFunc>0) {
    if (iFunc<=10) { //iFunc==11 -- update bubble text
      if (iMouseIsAtItem == iMouseWasAtItem || iMouseWasAtItem < 0)
        iCounter1+=BUBBLETIMERINTERVAL;
      else
        iCounter1=0;
      iMouseWasAtItem = iMouseIsAtItem;
      iSource=iFunc;
    }

    if ((iFunc<=10 && ((iCounter1 >= 400 /*ms*/&& iBubbleAtItem < 0) ||
        (iBubbleAtItem >= 0  && iBubbleAtItem != iMouseIsAtItem))) ||
        (iFunc==11 && iMouseIsAtItem==iBubbleAtItem)) {
      GetItemTitle(plswData->TaskArr[iMouseIsAtItem].hsw,ucTitle,sizeof(ucTitle),TRUE);
      hps = WinGetPS(plswData->hwndBubble);
      GpiQueryTextBox(hps,strlen(ucTitle),ucTitle,TXTBOX_COUNT,aptl);
      WinReleasePS(hps);
      if (iFunc==11) {
        WinQueryWindowPos(plswData->hwndBubble,&swp);
        usX = swp.x; usY = swp.y;
      }
      usSizeX = aptl[TXTBOX_TOPRIGHT].x-aptl[TXTBOX_TOPLEFT].x+8;
      usSizeY = aptl[TXTBOX_TOPLEFT].y-aptl[TXTBOX_BOTTOMLEFT].y+4;
      cxScreen=WinQuerySysValue(HWND_DESKTOP,SV_CXSCREEN);
      if (usX+usSizeX>cxScreen) usX-=usX+usSizeX-cxScreen;
      WinSetWindowPos(plswData->hwndBubble,HWND_TOP,
                      usX,usY,usSizeX,usSizeY,
                      SWP_MOVE | SWP_SIZE | SWP_SHOW | SWP_ZORDER);
      WinSetWindowPos(WinWindowFromID(plswData->hwndBubble,ID_BUBBLEFRAME),0,0,0,
                      usSizeX,usSizeY, SWP_MOVE | SWP_SIZE | SWP_SHOW);
      WinSetWindowText(plswData->hwndBubble,ucTitle);
      iBubbleAtItem = iMouseIsAtItem;
    } else if (iBubbleAtItem>=0 &&
               (WinQueryWindowPos(plswData->hwndBubble,&swp),
                aptl[0].x=swp.x,aptl[0].y=aptl[1].y=swp.y+swp.cy-1,aptl[1].x=swp.x+swp.cx-1,
                WinWindowFromPoint(HWND_DESKTOP,&aptl[0],FALSE)!=plswData->hwndBubble ||
                WinWindowFromPoint(HWND_DESKTOP,&aptl[1],FALSE)!=plswData->hwndBubble
              )) {
      WinSetWindowPos(plswData->hwndBubble,HWND_TOP,0,0,0,0,SWP_ZORDER);
      WinInvalidateRect(plswData->hwndBubble,NULL,TRUE);
    }
  } else if (abs(iFunc)==iSource) {
    if (WinIsWindowVisible(plswData->hwndBubble))
      WinSetWindowPos(plswData->hwndBubble,HWND_BOTTOM,0,0,0,0,SWP_HIDE | SWP_ZORDER);
    iBubbleAtItem = -1;
    iMouseWasAtItem = -1;
    iCounter1 = 0;
  }
}

BOOL IsWindowClass(HWND hwnd,UCHAR *pszClassName)
{ UCHAR ucBuf[128];

  WinQueryClassName(hwnd,sizeof(ucBuf),ucBuf);
  return (strcmp(ucBuf,pszClassName)==0);
}

PVOID GetSwitchList(HAB hab,BOOL bInit,ULONG *ulItemCount)
{ static PSWBLOCK pSwb=NULL;

  if (bInit) {
    if (pSwb!=NULL) return NULL;
    if ((*ulItemCount = WinQuerySwitchList(hab, NULL, 0))==0 ||
        (pSwb=malloc(sizeof(HSWITCH)+sizeof(SWENTRY)* *ulItemCount))==NULL) {
      return NULL;
    }
    /* get all switch entries in one call; calling WinQuerySwitchHandle/SwitchEntry
     for each window turns out to be noticeably slower */
    *ulItemCount = WinQuerySwitchList(hab,pSwb,sizeof(HSWITCH)+sizeof(SWENTRY)* *ulItemCount);
    return pSwb;
  } else {
    if (pSwb!=NULL) free(pSwb);
    pSwb=NULL;
    return NULL;
  }
}

BOOL IsMinToViewer(HWND hwnd,ULONG flopt)
{
  return
    ((flopt & SWP_MINIMIZE) &&
     WinQueryWindowUShort(hwnd,QWS_YMINIMIZE)>
     WinQuerySysValue(HWND_DESKTOP,SV_CYSCREEN));
}


VOID MinimizeAllRestoredWin(LSWDATA *plswData)
{ SHORT i;

  for (i = plswData->usItems-2; i >= 0; i--) {
    if (plswData->TaskArr[i].fl & (SWP_MINIMIZE | SWP_HIDE))
      continue;

    if (!ChangeWindowPos(plswData,i,CMD_HIDE)) ChangeWindowPos(plswData,i,CMD_MINIMIZE);
  }
}


VOID GetItemTitle(HSWITCH hsw,UCHAR *ucTitle,USHORT usLen,BOOL bSessNum)
{ SWCNTRL swctl;
  USHORT k;

  if (WinQuerySwitchEntry(hsw, &swctl)!=0) {
    strcpy(ucTitle,"");
    return;
  }

  if (bSessNum)
    sprintf(ucTitle,"%.*s (0x%X)",usLen-6,swctl.szSwtitle,swctl.idProcess);
  else
    strncpy(ucTitle,swctl.szSwtitle,usLen-1);

  for (k=0;k<strlen(ucTitle);k++)
    if (ucTitle[k]=='\r' || ucTitle[k]=='\n') ucTitle[k]=' ';
}


HPOINTER GetItemIcon(HWND hwnd)
{ HPOINTER hIcon;
  UCHAR *pszPath;
  HWND hwnd1;

  if ((hIcon=(HPOINTER)WinSendMsg(hwnd,WM_QUERYICON,0,0))!=NULLHANDLE)
    return hIcon;

  if (IsWindowClass((hwnd1=WinWindowFromID(hwnd,FID_CLIENT)),"SeamlessClass")) {
    pszPath = (UCHAR *)WinQueryWindowULong(hwnd1,0L);
    hIcon=WinLoadFileIcon(pszPath,FALSE);
  } else {
    ULONG ulItemCount;
    LONG k;
    PSWBLOCK pSwb;
    PID pid;
    TID tid;

    if ((pSwb=GetSwitchList(0,TRUE,&ulItemCount))!=NULL) {
      for (k=ulItemCount-1;k>=0 && hIcon==NULLHANDLE;k--) {
        WinQueryWindowProcess(hwnd,&pid,&tid);
        if (pSwb->aswentry[k].swctl.hwnd!=hwnd && pSwb->aswentry[k].swctl.idProcess==pid)
          hIcon=(HPOINTER)WinSendMsg(pSwb->aswentry[k].swctl.hwnd,WM_QUERYICON,0,0);
      }
      GetSwitchList(0,FALSE,NULL);
    }
  }

  if (hIcon == NULLHANDLE)
    hIcon = WinQuerySysPointer(HWND_DESKTOP,SPTR_PROGRAM,FALSE);

  return hIcon;
}

BOOL IsDesktop(HWND hwnd)
{
  return (hwnd==WinQueryWindow(HWND_DESKTOP,QW_BOTTOM) &&
          (IsWindowClass(hwnd,"wpFolder window")));
}


//Written by Staffan Ulfberg
char Match(char *string, char *pattern)
{
  for (; '*'^*pattern; ++pattern, ++string) {
    if (!*string)
      return (!*pattern);
    if (toupper(*string)^toupper(*pattern) && '?'^*pattern)
      return FALSE;
  }
  /* two-line patch to prevent *too* much recursiveness: */
  while('*' == pattern[1])
    pattern++;
  do {
    if ( Match(string, pattern + 1) )
      return TRUE;
  } while (*string++);
  return FALSE;
}


BOOL IsInSkipList(LSWSETTINGS *pSettings,UCHAR *ucTitle,BOOL bTskBar)
{ BOOL bFound;
  USHORT j;
  SKIPLIST *pSkipList;

  pSkipList = bTskBar ? &pSettings->SkipListTskBar : &pSettings->SkipListPopup;
  for (j = 0, bFound = FALSE;
       j < MAXITEMS && (*pSkipList)[j]!=NULL &&
         !(bFound=Match(ucTitle, (*pSkipList)[j]));
       j++);

  return bFound;
}


VOID InitTaskArr(LSWDATA *plswData,BOOL bFullScreen,BOOL bTaskBar)
{ ULONG sidCurr,ulItemCount,ulItem;
  PSWBLOCK pSwb;
  HENUM henum;
  HWND hwndNext,hwndActive;
  SWP swp;
  UCHAR ucIndex;
  SWITEM ditem;
  BOOL bFound,bIsCurrent;

  sidCurr = 0;
  hwndActive = NULLHANDLE;

  plswData->usItems = plswData->usCurrItem = plswData->iShift = 0;
  memset(plswData->TaskArr,0,sizeof(plswData->TaskArr));

  if ((pSwb=GetSwitchList(plswData->hab,TRUE,&ulItemCount))==NULL) return;
/* get SID of the current fullscreen process */
  if (bFullScreen) {
    if (DosQuerySysInfo(24,24,&sidCurr,sizeof(sidCurr))!=0) sidCurr=0;
  } else {
    hwndActive = WinQueryActiveWindow(HWND_DESKTOP);
  }

  henum = WinBeginEnumWindows(HWND_DESKTOP);

  while ((hwndNext = WinGetNextWindow(henum))!=NULLHANDLE && plswData->usItems < MAXITEMS-1) {
    for (ulItem = 0, bFound = FALSE; ulItem < ulItemCount; ulItem++)
      if ((bFound=(hwndNext == pSwb->aswentry[ulItem].swctl.hwnd))==TRUE) break;

    if (!bFound || !WinIsWindow(plswData->hab,hwndNext) ||
        pSwb->aswentry[ulItem].swctl.uchVisibility != SWL_VISIBLE)
      continue;

    WinQueryWindowPos(hwndNext,&swp);

    bFound = ((sidCurr == pSwb->aswentry[ulItem].swctl.idSession ||
               (((bTaskBar?plswData->Settings.bShowHiddenTskBar:plswData->Settings.bShowHidden) || !(swp.fl & SWP_HIDE)) &&
                ((bTaskBar?plswData->Settings.bShowViewerTskBar:plswData->Settings.bShowViewer) || !IsMinToViewer(hwndNext,swp.fl)))) &&
              !IsInSkipList(&plswData->Settings, pSwb->aswentry[ulItem].swctl.szSwtitle,bTaskBar));

    /* save the current entry in the last element of the array even if it's
     not supposed to be switched to. It will be used if switching is cancelled */
    bIsCurrent = ((!bFullScreen &&
                  (hwndNext == hwndActive ||
                   hwndNext == WinQueryWindow(hwndActive,QW_OWNER))) ||
                  (bFullScreen && pSwb->aswentry[ulItem].swctl.idSession == sidCurr));

    if (bFound) {
      ucIndex = plswData->usItems;
      plswData->usItems++;
    } else if (bIsCurrent)
      ucIndex = MAXITEMS-1;
    else
      continue;

    plswData->TaskArr[ucIndex].ulType = pSwb->aswentry[ulItem].swctl.bProgType;
    plswData->TaskArr[ucIndex].fl = swp.fl;
    plswData->TaskArr[ucIndex].hsw = pSwb->aswentry[ulItem].hswitch;
    plswData->TaskArr[ucIndex].hwnd = hwndNext;
    plswData->TaskArr[ucIndex].hIcon = GetItemIcon(hwndNext);
    plswData->TaskArr[ucIndex].pid = pSwb->aswentry[ulItem].swctl.idProcess;

    if (bFound && bIsCurrent) {
      memcpy(&plswData->TaskArr[MAXITEMS-1],&plswData->TaskArr[ucIndex],sizeof(SWITEM));
      /* make sure current session or window is in the 0th element of the TaskArr */
      if (ucIndex != 0) {
        memcpy(&ditem,&plswData->TaskArr[ucIndex],sizeof(ditem));
        memmove(&plswData->TaskArr[1],&plswData->TaskArr[0],sizeof(ditem)*ucIndex);
        memcpy(&plswData->TaskArr[0],&ditem,sizeof(ditem));
      }
    }
  }

  WinEndEnumWindows(henum);

  GetSwitchList(0,FALSE,NULL);

  if (plswData->TaskArr[MAXITEMS-1].hsw == 0)
    plswData->TaskArr[MAXITEMS-1].hwnd = hwndActive;
}

BOOL AddFilter(LSWSETTINGS *pSettings,UCHAR *ucName,BOOL bTskBar)
{ SHORT iNum;
  SKIPLIST *pSkipList;

  pSkipList = bTskBar?&pSettings->SkipListTskBar:&pSettings->SkipListPopup;

  if (strlen(ucName)==0 || IsInSkipList(pSettings,ucName,bTskBar)) return FALSE;

  for (iNum=0; (*pSkipList)[iNum]!=NULL && iNum<MAXITEMS; iNum++);
  if (iNum==MAXITEMS) return FALSE;

  (*pSkipList)[iNum++]=strdup(ucName);
  return TRUE;
}

BOOL RemoveFilter(LSWSETTINGS *pSettings,UCHAR *ucName,BOOL bTskBar)
{ SHORT iNum,k;
  SKIPLIST *pSkipList;

  pSkipList = bTskBar?&pSettings->SkipListTskBar:&pSettings->SkipListPopup;

  for (iNum=0; (*pSkipList)[iNum]!=NULL && iNum<MAXITEMS; iNum++);

  for (k = 0; k < iNum; k++)
    if (strcmp((*pSkipList)[k],ucName)==0) {
      free((*pSkipList)[k]);
      memmove(&(*pSkipList)[k], &(*pSkipList)[k+1],
              sizeof(pSettings->SkipListPopup[0])*(iNum-k-1));
      (*pSkipList)[--iNum]=NULL;
      return TRUE;
    }

  return FALSE;
}

USHORT RunCommand(LSWDATA *plswData,UCHAR *ucCommand,UCHAR *ucErrMsg,USHORT usErrMsgLen)
{ STARTDATA SData;
  UCHAR k,ucCmdLine[_MAX_PATH]="",ucPath[_MAX_PATH],ucPgmInp[_MAX_PATH],
    drive[_MAX_DRIVE],dir[_MAX_DIR],name[_MAX_FNAME],ext[_MAX_EXT],*args;
  static UCHAR ucExtStr[4][4]={"bat\0","cmd\0","com\0","exe\0"};
  APIRET rc;
  PID pid;
  ULONG ulSessID,ulAppType;

  if ((args=strchr(ucCommand,' '))!=NULL) {
    strcpy(ucCmdLine,args);
    *args='\0';
  }
  strcpy(ucPath,ucCommand);

  _splitpath(ucPath,drive,dir,name,ext);

  for (k=0,rc=0;k<(strlen(ext)==0?4:1);k++) { //add an extension if needed and find if file exists
    if (strlen(ext)==0) _makepath(ucPath,drive,dir,name,ucExtStr[k]);
    ulAppType=0;
    rc=DosQueryAppType(ucPath,&ulAppType);
    if (rc==0 || rc==191 || rc==193)
      break;
    else
      strncpy(ucPath,name,sizeof(ucPath));
  }

  memset(&SData,0,sizeof(SData));
  SData.Length  = sizeof(SData);
  SData.Related = SSF_RELATED_INDEPENDENT;
  SData.FgBg    = SSF_FGBG_FORE;
  SData.TraceOpt = SSF_TRACEOPT_NONE;
  SData.InheritOpt = SSF_INHERTOPT_SHELL;
  SData.PgmControl = SSF_CONTROL_VISIBLE;
  SData.SessionType = SSF_TYPE_DEFAULT;

  if (ulAppType==0x20 || strcmpi(ucPath + strlen(ucPath) - 4, ".BAT") == 0)
    SData.SessionType = SSF_TYPE_WINDOWEDVDM;

  if (ulAppType&(FAPPTYP_WINDOWSREAL|FAPPTYP_WINDOWSPROT|FAPPTYP_WINDOWSPROT31)) {
    SData.PgmName="WINOS2.COM";
    sprintf(ucPgmInp, "/3 %s %s", ucPath, ucCmdLine);
    SData.SessionType = PROG_31_ENHSEAMLESSCOMMON;
  } else if (rc!=0 || strcmpi(ucPath + strlen(ucPath) - 4, ".CMD") == 0) {
    sprintf(ucPgmInp, "/C%s %s", ucPath, ucCmdLine);
  } else {
    SData.PgmName = ucPath;
    strcpy(ucPgmInp, ucCmdLine);
  }
  SData.PgmInputs = ucPgmInp;

  if ((rc=DosStartSession(&SData, &ulSessID, &pid))!=0) {
    WinLoadString(plswData->hab,plswData->hmodRes,MSG_CANTEXECUTE,usErrMsgLen,ucErrMsg);
    strncat(ucErrMsg,SData.PgmName,usErrMsgLen-strlen(ucErrMsg)-1);
    return rc;
  }

  return 0;
}

VOID SetControlsFont(HWND hwnd,BOOL bDoTitleBar)
{ ULONG aulSysInfo[2]={0};
  UCHAR ucFont[FACESIZE];
  HWND hwndCtl;
  HENUM henum;

  DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_MINOR,aulSysInfo,sizeof(aulSysInfo));
  strcpy(ucFont,(aulSysInfo[0]==20 && aulSysInfo[1]>=40)?DEFTITLEFONT4:DEFTITLEFONT3);

  henum = WinBeginEnumWindows(hwnd);
  while ((hwndCtl=WinGetNextWindow(henum))!=NULLHANDLE) {
    if (!bDoTitleBar && IsWindowClass(hwndCtl,"#9")) continue;
    WinSetPresParam(hwndCtl,PP_FONTNAMESIZE,strlen(ucFont)+1,ucFont);
  }
  WinEndEnumWindows(henum);
}

VOID GetStartupDir(UCHAR *ucDir,USHORT usLen)
{ PPIB ppib;
  PTIB ptib;
  SHORT k;

#ifndef XWORKPLACE
  DosGetInfoBlocks(&ptib,&ppib);
  DosQueryModuleName(ppib->pib_hmte, usLen, ucDir);
#else
  DosQueryModuleName(hmodWidgetDll, usLen, ucDir);
#endif

  for (k = strlen(ucDir)-1; ucDir[k] != '\\' && k >= 0; k--);
  ucDir[k+1] = '\0';
}

BOOL queryAppInstance(VOID)
{ HEV hev;

  if (hev=0, DosOpenEventSem(SEMRUNNINGNAME,&hev))
    return FALSE;
  else {
    DosCloseEventSem(hev);
    return TRUE;
  }
}

BOOL UpdateWinFlags(ULONG *OldFlags,ULONG NewFlags)
{ BOOL bNeedUpdate;

  NewFlags &= (SWP_MINIMIZE|SWP_MAXIMIZE|SWP_RESTORE|SWP_SHOW|SWP_HIDE|
               SWP_ACTIVATE|SWP_DEACTIVATE);

  bNeedUpdate = (((NewFlags & SWP_MINIMIZE) && !(*OldFlags & SWP_MINIMIZE)) ||
                 ((NewFlags & SWP_ACTIVATE) && !(*OldFlags & SWP_ACTIVATE)) ||
                 ((NewFlags & SWP_DEACTIVATE) && !(*OldFlags & SWP_DEACTIVATE)) ||
                 ((NewFlags & SWP_SHOW) && !(*OldFlags & SWP_SHOW)) ||
                 ((NewFlags & SWP_HIDE) && !(*OldFlags & SWP_HIDE)) ||
                 ((NewFlags & SWP_RESTORE) && (*OldFlags & SWP_MINIMIZE)) ||
                 ((NewFlags & SWP_MAXIMIZE) && (*OldFlags & SWP_MINIMIZE))
                );

  *OldFlags |= NewFlags;

  if (NewFlags & SWP_MINIMIZE) *OldFlags &= (~(SWP_RESTORE|SWP_MAXIMIZE));
  if (NewFlags & SWP_RESTORE)  *OldFlags &= (~(SWP_MINIMIZE|SWP_MAXIMIZE));
  if (NewFlags & SWP_MAXIMIZE) *OldFlags &= (~(SWP_MINIMIZE|SWP_RESTORE));

  if (NewFlags & SWP_HIDE) *OldFlags &= (~SWP_SHOW);
  if (NewFlags & SWP_SHOW) *OldFlags &= (~SWP_HIDE);

  if (NewFlags & SWP_ACTIVATE)   *OldFlags &= (~SWP_DEACTIVATE);
  if (NewFlags & SWP_DEACTIVATE) *OldFlags &= (~SWP_ACTIVATE);

  return bNeedUpdate;
}

VOID MakeFitStr(HPS hps,UCHAR *ucStr,USHORT usStrLen, USHORT usStrWid)
{ USHORT k,usLen;
  POINTL aptl[TXTBOX_COUNT], ptlStart, *ptlStr;

  usLen = strlen(ucStr);
  ptlStr = malloc(sizeof(POINTL)*(usLen+1));
  ptlStart.x = ptlStart.y = 0;

  GpiQueryCharStringPosAt(hps, &ptlStart, 0, usLen, ucStr, NULL, ptlStr);
  if (ptlStr[usLen].x <= usStrWid) {
    free(ptlStr);
    return;
  }

  GpiQueryTextBox(hps,2,"..",TXTBOX_COUNT,aptl);

  k = usLen;
  do {
    k--;
  } while (ptlStr[k].x+(aptl[TXTBOX_BOTTOMRIGHT].x-aptl[TXTBOX_BOTTOMLEFT].x) >
           usStrWid && k > 0);

  ucStr[k]='\0';
  if (k > 0) strncat(ucStr,"..",usStrLen-k-2);

  free(ptlStr);
}

USHORT FindResDll(UCHAR *ucDllName, USHORT usNameLen, UCHAR *ucLang, UCHAR *ucLangStr, USHORT usLangStrLen)
{ static UCHAR ucDir[_MAX_PATH],ucName[_MAX_PATH],ucErr[32];
  static HDIR hdir = HDIR_CREATE;
  HMODULE hmodRes;
  FILEFINDBUF3 FindBuffer = {0};
  ULONG ulResultBufLen, ulFindCount;
  APIRET rc;
  RESVERPROC *ResVerProc;
  ULONG ulVer;

  ulResultBufLen = sizeof(FILEFINDBUF3);
  ulFindCount = 1;
  *ucLang = 0;

  if (hdir == HDIR_CREATE) {
    GetStartupDir(ucDir,sizeof(ucDir));
    sprintf(ucName,"%s*.dll",ucDir);
    rc = DosFindFirst(ucName, &hdir, FILE_NORMAL, &FindBuffer, ulResultBufLen, &ulFindCount, FIL_STANDARD);
  } else
    rc = DosFindNext(hdir, &FindBuffer, ulResultBufLen, &ulFindCount);

  if (rc == 0) {
    sprintf(ucName,"%s%s",ucDir,FindBuffer.achName);
    if (DosLoadModule(ucErr, sizeof(ucErr), ucName, &hmodRes)==0) {
      if (DosQueryProcAddr(hmodRes, 0, "QueryResourceVersion", &ResVerProc)==0 &&
          (ulVer = ResVerProc(ucLangStr,usLangStrLen),
            (LOUCHAR(LOUSHORT(ulVer)) >= LASTINIVERMAJOROK &&
             HIUCHAR(LOUSHORT(ulVer)) >= LASTINIVERMINOROK &&
             LOUCHAR(HIUSHORT(ulVer)) >= LASTINIREVISIONOK))) {
        *ucLang = HIUCHAR(HIUSHORT(ulVer));
        strncpy(ucDllName,ucName,usNameLen);
      }
      DosFreeModule(hmodRes);
    }
  } else {
    DosFindClose(hdir);
    hdir = HDIR_CREATE;
    return 0;
  }
  return 1;
}

HMODULE LoadResource(UCHAR ucLang, LSWDATA *plswData, BOOL bEngOk)
{ static UCHAR ucDllName[_MAX_PATH], ucLangStr[32], ucResLang, ucErr[32], ucFoundDllName[_MAX_PATH];
  HMODULE hmodRes;
  USHORT usFound = 0;

  while (FindResDll(ucDllName, sizeof(ucDllName), &ucResLang, ucLangStr, sizeof(ucLangStr)))
    if (ucResLang!=0) {
      if (ucResLang==ucLang)
        usFound = 1;
      else if (bEngOk && ucResLang==ENGLISH && usFound==0)
        usFound = 2;
      else
        continue;
      strcpy(ucFoundDllName,ucDllName);
    }

  if (usFound && DosLoadModule(ucErr, sizeof(ucErr), ucFoundDllName, &hmodRes)==0) {
    if ((plswData->haccAlt = WinLoadAccelTable(plswData->hab,hmodRes,ID_ALTACCELTABLE))==0 ||
        (plswData->haccCtrl = WinLoadAccelTable(plswData->hab,hmodRes,ID_CTRLACCELTABLE))==0 ||
        (plswData->haccNoAlt = WinLoadAccelTable(plswData->hab,hmodRes,ID_NOALTACCELTABLE))==0) {
      DosFreeModule(hmodRes);
      return 0;
    } else {
      plswData->Settings.ucLanguage = (usFound==1?ucLang:ENGLISH);
      return hmodRes;
    }
  } else
    return 0;
}

typedef struct _MSGSTRUCT {
  HWND hwnd;
  ULONG ulMsgid;
  MPARAM mpParam1, mpParam2;
  MRESULT mrRes;
  HEV hev;
} MSGSTRUCT;

typedef MSGSTRUCT *pMSGSTRUCT;

VOID SendMsgThread(VOID *pParm)
{ LSWDATA *plswData=NULL;
  HMQ hmq;

  DosGetNamedSharedMem((PVOID*)&plswData,SHAREMEM_NAME,PAG_READ | PAG_WRITE);
  hmq = WinCreateMsgQueue(plswData->hab, 0);
  DosFreeMem(plswData);

  ((pMSGSTRUCT)pParm)->mrRes=
     WinSendMsg(((pMSGSTRUCT)pParm)->hwnd,((pMSGSTRUCT)pParm)->ulMsgid,
                ((pMSGSTRUCT)pParm)->mpParam1,((pMSGSTRUCT)pParm)->mpParam2);
  DosPostEventSem(((pMSGSTRUCT)pParm)->hev);
}

BOOL WinSendMsgAsync(HWND hwnd, ULONG ulMsgid, MPARAM mpParam1, MPARAM mpParam2, MRESULT *mrRes)
{ int tid;
  MSGSTRUCT MsgStruct;
  BOOL bRet;

  MsgStruct.hwnd=hwnd;
  MsgStruct.ulMsgid=ulMsgid;
  MsgStruct.mpParam1=mpParam1;
  MsgStruct.mpParam2=mpParam2;

  DosCreateEventSem("\\SEM32\\SNDMSGSEM",&MsgStruct.hev,0,0);
  tid=_beginthread(SendMsgThread,NULL,0x4000,&MsgStruct);

  if (DosWaitEventSem(MsgStruct.hev,200)==640) { //timeout
    DosBeep(1000,10);
    DosKillThread(tid);
    *mrRes=0;
    bRet=FALSE;
  } else {
    *mrRes=MsgStruct.mrRes;
    bRet=TRUE;
  }

  DosCloseEventSem(MsgStruct.hev);

  return bRet;
}


