/*
 *      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 "lswitch.h"
#include "settings.h"
#include "taskbar.h"
#include "common.h"
#include "fsutil.h"
#include "prmdlg.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <ctype.h>



/* we subclass the window procedure of the popup window frame */
static MRESULT EXPENTRY FrameWndProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2)
{

  switch (msg) {
  case WM_ADJUSTWINDOWPOS: {
    SWP *pswp;
    LSWDATA *plswData;

#ifndef XWORKPLACE
    plswData = WinQueryWindowPtr(WinWindowFromID(hwnd,FID_CLIENT),0);
#else
    plswData = ((PXCENTERWIDGET)WinQueryWindowPtr(WinWindowFromID(hwnd,FID_CLIENT),0))->pUser;
#endif

    pswp = PVOIDFROMMP(mp1);

    if (!plswData->bNowActive) {
      /* make sure we are not brought to top or activated as a result of other
       windows repositioning */
      if (pswp->fl & SWP_ZORDER) pswp->fl &= ~SWP_ZORDER;
      if (pswp->fl & SWP_ACTIVATE) pswp->fl &= ~SWP_ACTIVATE;

      if (pswp->fl & SWP_SHOW)
        WinPostMsg(plswData->hwndPopup,WM_COMMAND,MPFROMSHORT(CMD_SHOWSETTINGS),MPFROM2SHORT(CMDSRC_OTHER,FALSE));
    }
  }
  default:
    return ((PFNWP)WinQueryWindowPtr(hwnd,0))(hwnd,msg,mp1,mp2);
  }
}

static MRESULT EXPENTRY PrtyDlgProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2)
{ static HWND hwndPrty;
  static PID pidPrty;

  switch (msg) {
  case WM_INITDLG: {
    USHORT usPrty;
    SHORT sPrtyC, sPrtyD;
    UCHAR ucBuf[128],ucTitle[NAMELEN];
    HPS hps;
    HWND hwndTBar;
    RECTL rcl;
    POINTL aptl[TXTBOX_COUNT];

    LSWDATA *plswData;

    SetControlsFont(hwnd,TRUE);

    plswData = (LSWDATA*)PVOIDFROMMP(mp2);
    hwndPrty = plswData->TaskArr[plswData->iMenuAtItem].hwnd;
    pidPrty = plswData->TaskArr[plswData->iMenuAtItem].pid;

    WinQueryDlgItemText(hwnd,FID_TITLEBAR,sizeof(ucBuf),ucBuf);
    GetItemTitle(plswData->TaskArr[plswData->iMenuAtItem].hsw,ucTitle,sizeof(ucTitle),TRUE);
    strncat(ucBuf,": ",sizeof(ucBuf)-strlen(ucTitle)-1);
    strncat(ucBuf,ucTitle,sizeof(ucBuf)-strlen(ucTitle)-1);

    hwndTBar=WinWindowFromID(hwnd,FID_TITLEBAR);
    hps=WinGetPS(hwndTBar);
    WinQueryWindowRect(hwndTBar,&rcl);
    GpiQueryTextBox(hps,1,"W",TXTBOX_COUNT,aptl);

    MakeFitStr(hps,ucBuf,sizeof(ucBuf), rcl.xRight-rcl.xLeft-aptl[TXTBOX_BOTTOMRIGHT].x-aptl[TXTBOX_BOTTOMLEFT].x);
    WinReleasePS(hps);

    WinSetDlgItemText(hwnd,FID_TITLEBAR,ucBuf);

    DosGetPrty(PRTYS_PROCESS, &usPrty, pidPrty);
    sPrtyC = (usPrty+PRTYD_MAXIMUM)>>8;
    sPrtyD = usPrty-(sPrtyC<<8);

    WinCheckButton(hwnd, sPrtyC==PRTYC_IDLETIME?RAD_IDLETIME:
                         sPrtyC==PRTYC_REGULAR?RAD_REGULAR:
                         sPrtyC==PRTYC_TIMECRITICAL?RAD_CRITICAL:RAD_FGNDSERVER,TRUE);

    WinSendDlgItemMsg(hwnd,SPIN_DELTA,SPBM_SETLIMITS,MPFROMLONG(PRTYD_MAXIMUM),MPFROMLONG(PRTYD_MINIMUM));
    WinSendDlgItemMsg(hwnd,SPIN_DELTA,SPBM_SETTEXTLIMIT,MPFROMSHORT(3),0);
    WinSendDlgItemMsg(hwnd,SPIN_DELTA,SPBM_SETCURRENTVALUE,MPFROMLONG(sPrtyD),0);

    break;
  }
  case WM_COMMAND:
    if (SHORT1FROMMP(mp1)==DID_OK) {
      SHORT sPrtyC, sPrtyD;
      BOOL bDesc;

      WinSendDlgItemMsg(hwnd,SPIN_DELTA,SPBM_QUERYVALUE,MPFROMP(&sPrtyD),MPFROM2SHORT(0,SPBQ_ALWAYSUPDATE));
      if (WinQueryButtonCheckstate(hwnd,RAD_IDLETIME)) sPrtyC=PRTYC_IDLETIME;
      else if (WinQueryButtonCheckstate(hwnd,RAD_REGULAR)) sPrtyC=PRTYC_REGULAR;
      else if (WinQueryButtonCheckstate(hwnd,RAD_CRITICAL)) sPrtyC = PRTYC_TIMECRITICAL;
      else sPrtyC = PRTYC_FOREGROUNDSERVER;

      bDesc = (WinQueryButtonCheckstate(hwnd,CHK_DESCENDANTS));

      WinPostMsg(hwndPrty,LSWM_SETPRIORITY,
                 MPFROM2SHORT(((sPrtyC<<8)+sPrtyD)|(bDesc?0x8000:0),PRTY_MAGIC),
                 MPFROMLONG(pidPrty));
    }
  default:
    return WinDefDlgProc(hwnd, msg, mp1, mp2);
  }
  return 0;
}

static MRESULT EXPENTRY RunDlgProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2)
{ static HWND hwndList;
  static UCHAR ucFName[_MAX_PATH];

  switch (msg) {
  case WM_INITDLG: {
    UCHAR *ucBuf;
    HFILE hFile;
    ULONG ulAction,ulcbRead,k,pos;
    APIRET rc;

    SetControlsFont(hwnd,FALSE);
    hwndList=WinWindowFromID(hwnd,CB_RUNCOMMAND);
    WinSendMsg(hwndList,EM_SETTEXTLIMIT,MPFROMSHORT(_MAX_PATH),0);
    GetStartupDir(ucFName,sizeof(ucFName));
    strncat(ucFName,HSTFILENAME,sizeof(ucFName)-strlen(ucFName)-1);
    rc=DosOpen(ucFName,&hFile,&ulAction,0,FILE_ARCHIVED|FILE_NORMAL,
               OPEN_ACTION_OPEN_IF_EXISTS|OPEN_ACTION_FAIL_IF_NEW,
               OPEN_FLAGS_FAIL_ON_ERROR|OPEN_SHARE_DENYWRITE|OPEN_ACCESS_READWRITE,0);
    if (rc==0) {
      if ((ucBuf=malloc((_MAX_PATH+2)*MAXRUNLISTHST))==NULL) {
        DosClose(hFile);
        break;
      }
      DosRead(hFile,ucBuf,(_MAX_PATH+2)*MAXRUNLISTHST,&ulcbRead);
      DosClose(hFile);
      for (k=0,pos=0;k<ulcbRead;k++)
        if (ucBuf[k]=='\n') {
          ucBuf[k]=ucBuf[k-1]='\0';
          WinSendMsg(hwndList,LM_INSERTITEM,MPFROMSHORT(LIT_END),MPFROMP(&ucBuf[pos]));
          pos=k+1;
        }
      free(ucBuf);
      WinSendMsg(hwndList,LM_SELECTITEM,MPFROMSHORT(0),MPFROMSHORT(TRUE));
    }

    break;
  }
  case WM_COMMAND:
    switch (SHORT1FROMMP(mp1)) {
    case DID_OK: {
      UCHAR ucCmd[_MAX_PATH+2],ucErrMsg[128],ucItemText[_MAX_PATH];
      USHORT k,usItemCount;
      SHORT sIndex;
      ULONG ulcbWrite,ulAction;
      APIRET rc;
      HFILE hFile;
      LSWDATA *plswData;

      WinQueryWindowText(hwndList,sizeof(ucCmd), ucCmd);
      strcpy(ucItemText,ucCmd); //RunCommand changes ucCmd

      if (DosGetNamedSharedMem((PVOID*)&plswData,SHAREMEM_NAME,PAG_READ | PAG_WRITE)==0 &&
          (rc=RunCommand(plswData,ucCmd,ucErrMsg,sizeof(ucErrMsg)))!=0) {
        WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,ucErrMsg,PGMNAME,0,MB_ERROR | MB_CANCEL);
        DosFreeMem(plswData);
        break;
      }

      sIndex=(SHORT)WinSendMsg(hwndList,LM_SEARCHSTRING,MPFROM2SHORT(0,LIT_FIRST),MPFROMP(ucItemText));
      if (sIndex!=LIT_NONE)
        WinSendMsg(hwndList,LM_DELETEITEM,MPFROMSHORT(sIndex),0);
      WinSendMsg(hwndList,LM_INSERTITEM,MPFROMSHORT(0),MPFROMP(ucItemText));

      rc=DosOpen(ucFName,&hFile,&ulAction,0,FILE_ARCHIVED|FILE_NORMAL,
                 OPEN_ACTION_REPLACE_IF_EXISTS|OPEN_ACTION_CREATE_IF_NEW,
                 OPEN_FLAGS_FAIL_ON_ERROR|OPEN_SHARE_DENYWRITE|OPEN_ACCESS_READWRITE,0);
      if (rc==0) {
        usItemCount=LONGFROMMR(WinSendMsg(hwndList,LM_QUERYITEMCOUNT,0,0));
        for (k=0;k<usItemCount && k<MAXRUNLISTHST;k++) {
          WinSendMsg(hwndList,LM_QUERYITEMTEXT,MPFROM2SHORT(k,sizeof(ucItemText)),MPFROMP(ucItemText));
          sprintf(ucCmd,"%s\r\n",ucItemText);
          DosWrite(hFile,ucCmd,strlen(ucCmd),&ulcbWrite);
        }
        DosClose(hFile);
      }

      return WinDefDlgProc(hwnd, msg, mp1, mp2);
    }
    case PB_BROWSE: {
      static FILEDLG fild={0};
      HWND hwndFDlg;
      SHORT k;

      fild.cbSize = sizeof(FILEDLG);
      fild.fl = FDS_CENTER | FDS_OPEN_DIALOG ;
      hwndFDlg = WinFileDlg(HWND_DESKTOP, hwnd, &fild);
      if (hwndFDlg!=NULLHANDLE && (fild.lReturn == DID_OK))
        WinSetWindowText(hwndList,(PCHAR)PVOIDFROMMP(fild.szFullFile));
      for (k=strlen(fild.szFullFile);k>=0;k--)
        if (fild.szFullFile[k]=='\\') {
          fild.szFullFile[k+1]='\0';
          strcat(fild.szFullFile,"*.EXE");
          break;
        }

      break;
    }
    case DID_CANCEL:
      WinSendDlgItemMsg(hwnd,CB_RUNCOMMAND,CBM_SHOWLIST,MPFROMSHORT(FALSE),0);
      return WinDefDlgProc(hwnd, msg, mp1, mp2);

    default:
      return WinDefDlgProc(hwnd, msg, mp1, mp2);
    }
    break;
  default:
    return WinDefDlgProc(hwnd, msg, mp1, mp2);
  }
  return 0;
}

/* This function returns the bottom left corner coords of the usNum number
  icon counting from the top left corner in the popup window */
VOID IconPosFromNumber(LSWDATA *plswData,USHORT usNum,USHORT *usPosX, USHORT *usPosY)
{ USHORT usLine,usLines,usGapX,usGapY,usIconsPerLine;
  RECTL rcl;
  ULONG cxIcon,cyIcon;

  usIconsPerLine = min(plswData->usItems,MAXICONSINLINE);
  usLine = usNum/MAXICONSINLINE+1;
  usLines = min(ICONLINES,(plswData->usItems-1)/MAXICONSINLINE+1);
  cxIcon = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
  cyIcon = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);
  usGapX = cxIcon*ICONSPACINGX;
  usGapY = cyIcon*ICONSPACINGY;

  WinQueryWindowRect(plswData->hwndPopClient,&rcl);

  *usPosX = rcl.xRight/2-
    cxIcon*(usIconsPerLine-(usIconsPerLine%2==0?1:0))/2-
    (usIconsPerLine-1)/2*usGapX+(usNum-(usLine-1)*MAXICONSINLINE)*(cxIcon+usGapX);

  *usPosY = rcl.yTop-2*cyIcon*SELBOXOFFSET-SELBOXLINEWIDTH-
    usLine*cyIcon-(usLine-1)*usGapY;
}


/* This function returns the number of the item in the TaskArr from the number
 of its icon counting from the top left corner in the popup window */
SHORT ItemNumFromIconNum(LSWDATA *plswData,SHORT iNum)
{ SHORT iItemNum;
  USHORT usLines;

  usLines = min(ICONLINES,(plswData->usItems-1)/MAXICONSINLINE+1);

  iItemNum = plswData->iShift+iNum-
    (min(MAXICONSINLINE,plswData->usItems)-1)/2-(usLines-1)/2*MAXICONSINLINE+
    (plswData->Settings.bScrollItems ? plswData->usCurrItem : 0);

  AdjustItem(iItemNum,plswData->usItems);

  return iItemNum;
}


/* This function returns the number of the icon in the popup window
 counting from the top left corner from the corresponding item number in the
 TaskArr. */
SHORT IconNumFromItemNum(LSWDATA *plswData, USHORT usNum)
{ USHORT usLines, usLastIconNum;
  SHORT iIconNum;

  usLastIconNum = min(plswData->usItems,MAXICONSINLINE*ICONLINES)-1;

  usLines = min(ICONLINES,(plswData->usItems-1)/MAXICONSINLINE+1);

  iIconNum = usNum-plswData->iShift+
    (min(MAXICONSINLINE,plswData->usItems)-1)/2+(usLines-1)/2*MAXICONSINLINE-
    (plswData->Settings.bScrollItems ? plswData->usCurrItem : 0);

  while (iIconNum > usLastIconNum || iIconNum < 0)
    if (iIconNum > usLastIconNum)
      iIconNum -= plswData->usItems;
    else if (iIconNum < 0)
      iIconNum += plswData->usItems;

  return iIconNum;
}


/* This function returns the number of the item in the TaskArr
 whose icon in the popup window is currently under the mouse pointer.
 -1 is returned if mouse pointer does not point on any icon*/
SHORT ItemNumFromPos(LSWDATA *plswData, USHORT usX, USHORT usY)
{ USHORT usItemNum;
  USHORT usPosX,usPosY,usGapX,usGapY,usCol,usRow;
  ULONG cxIcon,cyIcon;

  cxIcon = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
  cyIcon = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);
  usGapX = cxIcon*ICONSPACINGX;
  usGapY = cyIcon*ICONSPACINGY;

  IconPosFromNumber(plswData,0,&usPosX,&usPosY);
  usPosY+=cyIcon; //top left corner instead of the bottom left

  usCol = (usX-usPosX)/(cxIcon+usGapX)+1;
  usRow = (usPosY-usY)/(cyIcon+usGapY)+1;

  usItemNum = ItemNumFromIconNum(plswData,(usRow-1)*MAXICONSINLINE+usCol-1);
  /* check if mouse points on an icon */
  if ((usRow-1)*MAXICONSINLINE+usCol<=plswData->usItems &&
      usX>=usPosX+(usCol-1)*(cxIcon+usGapX) && usX<=usPosX+usCol*(cxIcon+usGapX)-usGapX &&
      usY<=usPosY-(usRow-1)*(cyIcon+usGapY) && usY>=usPosY-usRow*(cyIcon+usGapY)+usGapY)
    return usItemNum;
  else
    return -1;
}



VOID MakeHintStr(LSWDATA *plswData,USHORT usItem,UCHAR *ucStr,USHORT usStrLen)
{ UCHAR ucBuf[32],ucCmd[32],*pucTilde,ucHot;
  USHORT k,l;

  WinLoadString(plswData->hab,plswData->hmodRes,STRID_HINTS,usStrLen,ucStr);

  for (k = CMD_HIDE; k <= CMD_CLOSE; k++)
    if (MenuNeedsItem(plswData,usItem,k,ucBuf,sizeof(ucBuf)) && k!=CMD_MOVE) {
      if ((pucTilde = strchr(ucBuf,'~'))!=NULL) {
        memmove(pucTilde,pucTilde+1,strlen(pucTilde));
        if (plswData->Settings.ucLanguage == RUSSIAN) {
          if (*pucTilde >= 160 && *pucTilde <= 175)
            ucHot = *pucTilde - 32;
          else if (*pucTilde >= 224 && *pucTilde <= 239)
            ucHot = *pucTilde - 80;
          else
            ucHot = *pucTilde;
        } else
          ucHot = toupper(*pucTilde);
      }

      if (plswData->Settings.ucLanguage == RUSSIAN) {
        for (l=0; l < strlen(ucBuf); l++)
          if (ucBuf[l] >= 128 && ucBuf[l]<= 143)
            ucBuf[l] += 32;
          else if (ucBuf[l] >= 144 && ucBuf[l] <= 159)
            ucBuf[l] += 80;
      } else if (plswData->Settings.ucLanguage != GERMAN)
        strlwr(ucBuf);

      sprintf(ucCmd,"  %c:%s",ucHot,ucBuf);
      strncat(ucStr,ucCmd,usStrLen-strlen(ucStr)-1);
    }
}


VOID AdjustPopupWin(LSWDATA *plswData,BOOL bInit)
{ USHORT cxPopup,cyPopup,cxIcon,cyIcon,usLines,usPosX,usPosY,
    usTxtHgtTitle,usTxtHgtHints;
  RECTL rcl;
  HWND hwndTitle,hwndHints;
  HPS hps;
  POINTL aptl[TXTBOX_COUNT];
  UCHAR ucBuf[2*NAMELEN];

  cxIcon = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
  cyIcon = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);

  hwndTitle = WinWindowFromID(plswData->hwndPopClient,ID_TITLESTR);
  hwndHints = WinWindowFromID(plswData->hwndPopClient,ID_HINTSTR);

  if (bInit) {
    WinSetPresParam(plswData->hwndPopClient,PP_BACKGROUNDCOLORINDEX,
                    sizeof(plswData->Settings.lBackColor),
                    &plswData->Settings.lBackColor);

    WinSetPresParam(hwndTitle,PP_FONTNAMESIZE,
                    strlen(plswData->Settings.ucTitleFont)+1,
                    plswData->Settings.ucTitleFont);
    WinSetPresParam(hwndTitle,PP_BACKGROUNDCOLORINDEX,
                    sizeof(plswData->Settings.lBackColor),
                    &plswData->Settings.lBackColor);
    WinSetPresParam(hwndTitle,PP_FOREGROUNDCOLORINDEX,
                    sizeof(plswData->Settings.lTitleColor),
                    &plswData->Settings.lTitleColor);

    WinSetPresParam(hwndHints,PP_BACKGROUNDCOLORINDEX,
                    sizeof(plswData->Settings.lBackColor),
                    &plswData->Settings.lBackColor);
    WinSetPresParam(hwndHints,PP_FOREGROUNDCOLORINDEX,
                    sizeof(plswData->Settings.lHintsColor),
                    &plswData->Settings.lHintsColor);

    WinSetAccelTable(plswData->hab,plswData->Settings.bStickyPopup?plswData->haccNoAlt:
                     plswData->Settings.bUseCtrl?plswData->haccCtrl:plswData->haccAlt,
                     plswData->hwndPopup);
  }

  hps = WinGetPS(hwndTitle);
  GpiQueryTextBox(hps,1L,"M",TXTBOX_COUNT,aptl);
  WinReleasePS(hps);
  usTxtHgtTitle = aptl[TXTBOX_TOPLEFT].y-aptl[TXTBOX_BOTTOMLEFT].y;

  hps = WinGetPS(hwndHints);
  GpiQueryTextBox(hps,1L,"M",TXTBOX_COUNT,aptl);
  WinReleasePS(hps);
  usTxtHgtHints = aptl[TXTBOX_TOPLEFT].y-aptl[TXTBOX_BOTTOMLEFT].y;

  GetItemTitle(plswData->TaskArr[plswData->usCurrItem].hsw,ucBuf,sizeof(ucBuf),TRUE);
  WinSetWindowText(hwndTitle,ucBuf);

  MakeHintStr(plswData,plswData->usCurrItem,ucBuf,sizeof(ucBuf));
  WinSetWindowText(hwndHints,ucBuf);

  cxPopup = cxIcon*MAXICONSINLINE+cxIcon*(MAXICONSINLINE+2)*ICONSPACINGX;
  usLines = plswData->Settings.bOldPopup ? 1 :
    min(ICONLINES,(plswData->usItems-1)/MAXICONSINLINE+1);
  cyPopup = usLines*cyIcon+(usLines-1)*cyIcon*ICONSPACINGY+
    usTxtHgtTitle*
      ((plswData->Settings.bShowHints || plswData->Settings.bOldPopup) ? 2: 3)/2+
    ((plswData->Settings.bShowHints && !plswData->Settings.bOldPopup) ? usTxtHgtHints : 0)+
    4*cyIcon*SELBOXOFFSET+2*SELBOXLINEWIDTH+
    2*WinQuerySysValue(HWND_DESKTOP,SV_CYDLGFRAME);

  WinSetWindowPos(plswData->hwndPopup, HWND_TOP,
                  (WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN)-cxPopup)/2,
                  (WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN)-cyPopup)/2,
                  cxPopup, cyPopup,
                  SWP_MOVE | SWP_SIZE |
                  (bInit? (SWP_ACTIVATE | SWP_ZORDER | SWP_SHOW) : 0));
  // not sure if this is needed but sometimes the popup window hides immediately
  // after showing. Maybe it doesn't get focus and WM_TIMER hides it, let's
  // observe if this happens ever is we set the focus explicitly
  if (bInit) WinSetFocus(HWND_DESKTOP,plswData->hwndPopClient);

  WinQueryWindowRect(plswData->hwndPopClient,&rcl);

  if (plswData->Settings.bOldPopup) {
    WinSetWindowPos(hwndTitle, 0, cxIcon+cxIcon*ICONSPACINGX, 1,
                    rcl.xRight-cxIcon-cxIcon*ICONSPACINGX-2, rcl.yTop-2,
                    SWP_SIZE | SWP_MOVE);
    WinSetWindowPos(hwndHints,0,0,0,0,0,SWP_SIZE | SWP_MOVE);
  } else {
    IconPosFromNumber(plswData, (usLines-1)*MAXICONSINLINE, &usPosX,&usPosY);
    WinSetWindowPos(hwndTitle, 0,
                    1,1+(plswData->Settings.bShowHints ? usTxtHgtHints : 0),
                    rcl.xRight-2,
                    usTxtHgtTitle*(plswData->Settings.bShowHints ? 2 : 3)/2,
                    SWP_SIZE | SWP_MOVE);
    WinSetWindowPos(hwndHints,0,
                    plswData->Settings.bShowHints ? 1 : 0,
                    plswData->Settings.bShowHints ? 1 : 0,
                    plswData->Settings.bShowHints ? rcl.xRight-2 : 0,
                    plswData->Settings.bShowHints ? usTxtHgtHints : 0,
                    SWP_SIZE | SWP_MOVE);
  }

  WinInvalidateRect(plswData->hwndPopClient,NULL,TRUE);
}


/* popup client window procedure */
static MRESULT EXPENTRY PopupWndProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2)
{ static ULONG cxIcon,cyIcon,cyPointer;
  static HWND hwndTitle,hwndHints;
  static bNowSticky=FALSE;
  LSWDATA *plswData;
  MRESULT mrc;

  mrc=0;

  if (msg!=WM_CREATE)
#ifndef XWORKPLACE
    plswData = WinQueryWindowPtr(hwnd,0);
#else
    plswData = ((PXCENTERWIDGET)WinQueryWindowPtr(hwnd,0))->pUser;
#endif

  switch (msg) {
  case WM_BUTTON1CLICK: {
    SHORT iItemNum;

    if (!plswData->Settings.bOldPopup &&
        (iItemNum=ItemNumFromPos(plswData,SHORT1FROMMP(mp1),SHORT2FROMMP(mp1)))>=0) {
      plswData->usCurrItem=iItemNum;
      WinInvalidateRect(hwnd,NULL,FALSE);
      DosSleep(50);
      WinPostMsg(plswData->hwndPopup,WM_COMMAND,MPFROMSHORT(CMD_SWITCHFROMPM),
                   MPFROM2SHORT(CMDSRC_OTHER,TRUE));

      mrc=(MRESULT)TRUE;
    }
    break;
  }

  case WM_BUTTON2CLICK:
    if (!plswData->Settings.bOldPopup &&
        (plswData->iMenuAtItem=
         ItemNumFromPos(plswData,SHORT1FROMMP(mp1),SHORT2FROMMP(mp1)))>=0) {
      ShowMenu(plswData, plswData->iMenuAtItem,FALSE);
      ShowBubble(plswData,0,0,0,-1);

      mrc=(MRESULT)TRUE;
    }
    break;

  case WM_CHAR: {
    USHORT usFlags;
    UCHAR ucScan;

    usFlags = SHORT1FROMMP(mp1); ucScan = CHAR4FROMMP(mp1);
    if (plswData->bNowActive && (usFlags & KC_SCANCODE) && (usFlags & KC_KEYUP)) {

      if (ucScan==(plswData->Settings.bUseCtrl?SCAN_ALT:SCAN_CTRL)||
          ucScan==(plswData->Settings.bUseCtrl?SCAN_RIGHTALT:SCAN_RIGHTCTRL)) {
        bNowSticky=TRUE;
        WinSetAccelTable(plswData->hab,plswData->haccNoAlt, plswData->hwndPopup);
        break;
      }

      if (ucScan==((plswData->Settings.bStickyPopup||bNowSticky)?SCAN_ENTER:
                   plswData->Settings.bUseCtrl?SCAN_CTRL:SCAN_ALT)) {
        /* left alt or ctrl release, switch to the selected window */
        WinPostMsg(plswData->hwndPopup,WM_COMMAND,MPFROMSHORT(CMD_SWITCHFROMPM),
                   MPFROM2SHORT(CMDSRC_OTHER,FALSE));
      }
    }
    break;
  }

  case WM_COMMAND:
    switch (SHORT1FROMMP(mp1)) {
    case CMD_SCROLLLEFT:
    case CMD_SCROLLRIGHT:
    case CMD_SCROLLUP:
    case CMD_SCROLLDOWN:
    case CMD_SCROLLLEFTFROMFS:
    case CMD_SCROLLRIGHTFROMFS: {
      USHORT usPrevSel, usMaxIcons, usPosX, usPosY;
      RECTL rcl,rcl1,rcl2;
      BOOL bUpdateAll,bFullScreenInit,bForward,bNeedScroll;
      UCHAR ucBuf[2*NAMELEN];
      SHORT iCurrItem;

      bFullScreenInit = SHORT1FROMMP(mp1)==CMD_SCROLLLEFTFROMFS ||
                        SHORT1FROMMP(mp1)==CMD_SCROLLRIGHTFROMFS;

      if (!plswData->bNowActive) {
        InitTaskArr(plswData,bFullScreenInit,FALSE);
        if (plswData->usItems == 0) break;
      }

      usMaxIcons = MAXICONSINLINE*ICONLINES;

      usPrevSel = plswData->usCurrItem;
      bForward = SHORT1FROMMP(mp1)==CMD_SCROLLRIGHT ||
                 SHORT1FROMMP(mp1)==CMD_SCROLLRIGHTFROMFS ||
                 SHORT1FROMMP(mp1)==CMD_SCROLLDOWN;
      bNeedScroll = FALSE;

      switch SHORT1FROMMP(mp1) {
      case CMD_SCROLLLEFT:
      case CMD_SCROLLLEFTFROMFS:  iCurrItem = usPrevSel - 1; break;
      case CMD_SCROLLRIGHT:
      case CMD_SCROLLRIGHTFROMFS: iCurrItem = usPrevSel + 1; break;
      case CMD_SCROLLUP:
      case CMD_SCROLLDOWN: {
        SHORT iRow;
        USHORT usRows,usCol,usIconNum;

        usIconNum = IconNumFromItemNum(plswData,usPrevSel);
        usRows = min(ICONLINES,(plswData->usItems-1)/MAXICONSINLINE+1);
        if (usRows == 1) { iCurrItem = usPrevSel; break; }
        iRow = usIconNum/MAXICONSINLINE;
        usCol = usIconNum-iRow*MAXICONSINLINE;

        if (plswData->usItems <= usMaxIcons) {
          if (SHORT1FROMMP(mp1) == CMD_SCROLLUP)
            if (iRow == 0) iRow = usRows-1; else iRow--;
          else
            if (iRow == usRows-1) iRow = 0; else iRow++;
          iCurrItem = usPrevSel+min(iRow*MAXICONSINLINE+usCol,plswData->usItems-1)-
                      usIconNum;
        } else {
          iCurrItem = usPrevSel +
            (SHORT1FROMMP(mp1) == CMD_SCROLLUP ? -MAXICONSINLINE : MAXICONSINLINE);
          bNeedScroll = (iRow==ICONLINES-1 && bForward) || (iRow==0 && !bForward);
        }
        break;
      }
      }

      AdjustItem(iCurrItem,plswData->usItems);
      plswData->usCurrItem = iCurrItem;

      /* check if the number of items is larger than fits in the window and we are
         at a window end so we need to scroll */
      if (!plswData->Settings.bScrollItems && plswData->usItems>usMaxIcons &&
          (plswData->usCurrItem ==
           ItemNumFromIconNum(plswData,bForward ? usMaxIcons : -1) ||
           bNeedScroll)) {
        plswData->iShift += (SHORT)(bForward ? MAXICONSINLINE : -MAXICONSINLINE);
        plswData->iShift -= plswData->iShift/(SHORT)plswData->usItems*plswData->usItems;
        bUpdateAll = TRUE;
      } else
        bUpdateAll = FALSE;

      if (!plswData->bNowActive) {
        // this must be just before adjusting the window, otherwise if it's after,
        // the subclassed frame window proc won't let us do repositioning;
        // if it's much before, the WM_TIMER might have time to hide the window
        plswData->bNowActive = TRUE; bNowSticky = FALSE;

        if (bFullScreenInit)  WinSwitchToProgram(plswData->hswitch);

        AdjustPopupWin(plswData,TRUE);

      } else {
        if (plswData->Settings.bScrollItems || plswData->Settings.bOldPopup ||
            bUpdateAll) {
          WinQueryWindowRect(hwnd,&rcl);
          if (plswData->Settings.bOldPopup) {
            WinSetRect(plswData->hab,&rcl,1,1,rcl.xRight-2,rcl.yTop-2);
          } else {
            WinQueryWindowRect(hwndHints,&rcl1);
            WinSetRect(plswData->hab,&rcl,1,1+rcl1.yTop,rcl.xRight-2,rcl.yTop-2);
          }
        } else {
          IconPosFromNumber(plswData, IconNumFromItemNum(plswData,usPrevSel),
                            &usPosX,&usPosY);
          WinSetRect(plswData->hab,&rcl1, usPosX-cxIcon*SELBOXOFFSET-SELBOXLINEWIDTH,
                     usPosY-cyIcon*SELBOXOFFSET-SELBOXLINEWIDTH,
                     usPosX+cxIcon+cxIcon*SELBOXOFFSET+SELBOXLINEWIDTH,
                     usPosY+cyIcon+cyIcon*SELBOXOFFSET+SELBOXLINEWIDTH);
          IconPosFromNumber(plswData, IconNumFromItemNum(plswData,plswData->usCurrItem),
                            &usPosX,&usPosY);
          WinSetRect(plswData->hab,&rcl2, usPosX-cxIcon*SELBOXOFFSET-SELBOXLINEWIDTH,
                     usPosY-cyIcon*SELBOXOFFSET-SELBOXLINEWIDTH,
                     usPosX+cxIcon+cxIcon*SELBOXOFFSET+SELBOXLINEWIDTH,
                     usPosY+cyIcon+cyIcon*SELBOXOFFSET+SELBOXLINEWIDTH);
          WinUnionRect(plswData->hab,&rcl,&rcl1,&rcl2);
        }

        WinInvalidateRect(hwnd,&rcl,FALSE);

        GetItemTitle(plswData->TaskArr[plswData->usCurrItem].hsw,ucBuf,sizeof(ucBuf),TRUE);
        WinSetWindowText(hwndTitle,ucBuf);

        MakeHintStr(plswData,plswData->usCurrItem,ucBuf,sizeof(ucBuf));
        WinSetWindowText(hwndHints,ucBuf);
      }
      break;
    }

    case CMD_CANCELPOPUP:
    case CMD_CANCELPOPUP1:
      plswData->usCurrItem = MAXITEMS-1;

    case CMD_SWITCHFROMPM:
      if (plswData->bNowActive) {
        UCHAR ucBuf[128];

        WinSetWindowPos(plswData->hwndPopup,HWND_BOTTOM, POPUPWINHIDEPOSX,
                        POPUPWINHIDEPOSY, 0, 0, SWP_MOVE | SWP_SIZE | SWP_ZORDER);
        ShowBubble(plswData,0,0,0,-1);
        WinQueryPresParam(hwndTitle,PP_FONTNAMESIZE,0,NULL,sizeof(ucBuf),ucBuf,QPF_NOINHERIT);
        strncpy(plswData->Settings.ucTitleFont,ucBuf,sizeof(plswData->Settings.ucTitleFont));
        WinQueryPresParam(hwndHints,PP_FONTNAMESIZE,0,NULL,sizeof(ucBuf),ucBuf,QPF_NOINHERIT);
        strncpy(plswData->Settings.ucHintsFont,ucBuf,sizeof(plswData->Settings.ucHintsFont));
      }

      if (SHORT1FROMMP(mp2)==CMDSRC_MENU) plswData->usCurrItem = plswData->iMenuAtItem;

    case CMD_SWITCHFROMFS:
      /* turn the flag off only after the window has been repositioned or
       our subclassed frame window procedure won't let do this */
      plswData->bNowActive = FALSE;

      if (SHORT1FROMMP(mp1) != CMD_CANCELPOPUP && SHORT1FROMMP(mp1) != CMD_CANCELPOPUP1) {
        if (plswData->Settings.bDesktopMinimizes && IsDesktop(plswData->TaskArr[plswData->usCurrItem].hwnd))
          MinimizeAllRestoredWin(plswData);

//        if ((plswData->TaskArr[plswData->usCurrItem].fl & SWP_HIDE) ||
//            plswData->TaskArr[plswData->usCurrItem].hsw == plswData->hswitch)
// for some weird reason WinSwitchToProgram does not always bring the window to top
// showing is needed for hidden windows and won't hurt in other cases
          WinSetWindowPos(plswData->TaskArr[plswData->usCurrItem].hwnd,HWND_TOP,0,0,0,0,SWP_SHOW|SWP_ZORDER);
      }
      /* don't switch if the popup has been cancelled and the current program
       has been minimized or hidden or if a close window query has popped up and
       changed the focus to itself (and the POPUP_CANCEL has been triggered by
       the WM_TIMER) after the close command has been sent to a window */

      if (SHORT1FROMMP(mp1) != CMD_CANCELPOPUP1) {
        if (SHORT1FROMMP(mp1) != CMD_CANCELPOPUP ||
            ((plswData->TaskArr[plswData->usCurrItem].fl & (SWP_HIDE | SWP_MINIMIZE))==0) ||
            ((plswData->TaskArr[plswData->usCurrItem].ulType==PROG_FULLSCREEN ||
              plswData->TaskArr[plswData->usCurrItem].ulType==PROG_VDM) &&
             plswData->Settings.bPMPopupInFS))

        if (plswData->TaskArr[plswData->usCurrItem].hsw != 0)
          WinSwitchToProgram(plswData->TaskArr[plswData->usCurrItem].hsw);
        else
          WinSetWindowPos(plswData->TaskArr[plswData->usCurrItem].hwnd,
                          HWND_TOP,0,0,0,0,SWP_ACTIVATE | SWP_ZORDER);

/* this is needed to make a full screen session current */
        if  (plswData->TaskArr[plswData->usCurrItem].ulType==PROG_FULLSCREEN ||
             plswData->TaskArr[plswData->usCurrItem].ulType==PROG_VDM)
          WinSetWindowPos(plswData->TaskArr[plswData->usCurrItem].hwnd,HWND_TOP,
                          0,0,0,0,SWP_ZORDER | SWP_ACTIVATE);
      }
      break;

    case CMD_SHOWSETTINGS:
      if (!plswData->bNowActive) EditSettings(plswData);
      break;

    case CMD_PRIORITY:
      if (SHORT1FROMMP(mp2)==CMDSRC_ACCELERATOR) {
        if (!MenuNeedsItem(plswData,plswData->usCurrItem,CMD_PRIORITY,NULL,0))
          break;

        plswData->iMenuAtItem = plswData->usCurrItem;
      }

      WinDlgBox(HWND_DESKTOP,HWND_DESKTOP,PrtyDlgProc,plswData->hmodRes,DLG_PRTY,plswData);
      break;

    case CMD_ADDFILTER: {
      ENTRYNAME ucName;

      if (SHORT1FROMMP(mp2)==CMDSRC_ACCELERATOR)
        plswData->iMenuAtItem = plswData->usCurrItem;

      GetItemTitle(plswData->TaskArr[plswData->iMenuAtItem].hsw,ucName,sizeof(ucName),FALSE);
      if (AddFilter(&plswData->Settings,ucName,FALSE)) {
        USHORT k;
        ENTRYNAME ucName1;

        for (k=0;k<plswData->usItems;k++) { //check for entries with the same name
          GetItemTitle(plswData->TaskArr[k].hsw,ucName1,sizeof(ucName1),FALSE);
          if (strcmp(ucName,ucName1)==0)
            WinPostMsg(hwnd,LSWM_SWITCHLISTCHANGED, MPFROMLONG(2), MPFROMLONG(plswData->TaskArr[k].hsw));
        }
      }

      break;
    }

    case CMD_HIDE:
    case CMD_SHOW:
    case CMD_RESTORE:
    case CMD_MINIMIZE:
    case CMD_MAXIMIZE:
    case CMD_MOVE:
    case CMD_CLOSE:
    case CMD_KILL:
    case CMD_DEATH:
    case CMD_CLOSEQUIT:
      /* if this command comes from the keyboard use plswData->usCurrItem */
      ChangeWindowPos(plswData, SHORT1FROMMP(mp2)==CMDSRC_MENU ?
                      plswData->iMenuAtItem : plswData->usCurrItem,
                      SHORT1FROMMP(mp1));
      break;

    case CMD_RUN: {
      static HWND hwndRunDlg=NULLHANDLE;

      if (!WinIsWindow(plswData->hab,hwndRunDlg))
        hwndRunDlg=WinLoadDlg(HWND_DESKTOP,NULLHANDLE,RunDlgProc,plswData->hmodRes,DLG_RUN,NULL);
      if (hwndRunDlg != NULLHANDLE) {
        WinSetWindowPos(hwndRunDlg,HWND_TOP,0,0,0,0,SWP_SHOW | SWP_ZORDER | SWP_ACTIVATE);
        WinProcessDlg(hwndRunDlg);
      }
      break;
    }

    case CMD_QUIT:
      WinPostMsg(hwnd, WM_QUIT, 0, 0);
      break;
    }
    break;

  case WM_CREATE: {
    DosGetNamedSharedMem((PVOID*)&plswData,SHAREMEM_NAME,PAG_READ | PAG_WRITE);

    WinStartTimer(plswData->hab, hwnd, 0, BUBBLETIMERINTERVAL);

    plswData->hwndMenu = WinCreateMenu(HWND_DESKTOP, NULL);

    WinSetPresParam(plswData->hwndMenu,PP_FONTNAMESIZE,7,"8.Helv");

    hwndTitle = WinCreateWindow(hwnd,WC_STATIC,"",
                                WS_VISIBLE | SS_TEXT | DT_CENTER | DT_VCENTER,
                                0,0,0,0,hwnd,HWND_TOP,ID_TITLESTR,NULL,NULL);

    hwndHints = WinCreateWindow(hwnd,WC_STATIC,"",
                                WS_VISIBLE | SS_TEXT | DT_CENTER | DT_VCENTER,
                                0,0,0,0,hwnd,HWND_TOP,ID_HINTSTR,NULL,NULL);
    WinSetPresParam(hwndHints,PP_FONTNAMESIZE,strlen(plswData->Settings.ucHintsFont)+1,plswData->Settings.ucHintsFont);

    plswData->hwndBubble = WinCreateWindow(HWND_DESKTOP,WC_STATIC,"",
                                           SS_TEXT | DT_CENTER | DT_VCENTER,
                                           0,0,0,0,0,HWND_BOTTOM,ID_BUBBLE,NULL,NULL);
    WinSetPresParam(plswData->hwndBubble,PP_FONTNAMESIZE,7,"8.Helv");

    WinCreateWindow(plswData->hwndBubble,WC_STATIC,"",
                    SS_HALFTONEFRAME,
                    0,0,0,0,0,HWND_TOP,ID_BUBBLEFRAME,NULL,NULL);
#ifndef XWORKPLACE
    WinSetWindowPtr(hwnd, 0, plswData);
#else
    WinSetWindowPtr(hwnd, 0, plswData->pWidget);
#endif
    DosFreeMem(plswData);

  }
  case WM_SYSVALUECHANGED:
    cxIcon = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
    cyIcon = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);
    cyPointer=WinQuerySysValue(HWND_DESKTOP, SV_CYPOINTER);
    break;

  case WM_ERASEBACKGROUND:
    mrc = (MRESULT)FALSE;
    break;

  case WM_MENUEND:
    WinSetWindowPos(plswData->hwndMenu,HWND_BOTTOM,0,0,0,0,SWP_ZORDER);
    if (plswData->bNowActive) WinSetFocus(HWND_DESKTOP,hwnd);
    break;

  case WM_PAINT: {
    HPS hps;
    RECTL rclUpdate, rclWnd, rclSelBox;
    USHORT k,usPosX,usPosY,usItemNum;

    hps = WinBeginPaint(hwnd,NULLHANDLE,&rclUpdate);
    WinQueryWindowRect(hwnd, &rclWnd);

    WinFillRect(hps,&rclUpdate,plswData->Settings.lBackColor);
    WinDrawBorder(hps,&rclWnd,1,1,plswData->Settings.lSunkenFrameColor,
                  plswData->Settings.lRaisedFrameColor,DB_DEPRESSED);

    if (plswData->Settings.bOldPopup) {
      WinDrawPointer(hps,rclWnd.xLeft+cxIcon*ICONSPACINGX,
                     (rclWnd.yTop+rclWnd.yBottom-cyIcon)/2,
                     plswData->TaskArr[plswData->usCurrItem].hIcon,DP_NORMAL);
    } else {
      /* Draw icons */
      for (k = 0; k < min(MAXICONSINLINE*ICONLINES,plswData->usItems); k++) {

        IconPosFromNumber(plswData,k,&usPosX,&usPosY);
        usItemNum = ItemNumFromIconNum(plswData,k);

        WinDrawPointer(hps, usPosX, usPosY, plswData->TaskArr[usItemNum].hIcon,DP_NORMAL|
                       (plswData->TaskArr[usItemNum].fl&(SWP_HIDE|SWP_MINIMIZE))?DP_HALFTONED:0);

/* Draw selection box */
        if (usItemNum == plswData->usCurrItem) {
          WinSetRect(plswData->hab,&rclSelBox,
            usPosX-cxIcon*SELBOXOFFSET-1, usPosY-cyIcon*SELBOXOFFSET-1,
            usPosX+cxIcon*SELBOXOFFSET+cxIcon+1,usPosY+cyIcon*SELBOXOFFSET+cyIcon+1);
          WinDrawBorder(hps,&rclSelBox,SELBOXLINEWIDTH,SELBOXLINEWIDTH,
                        plswData->Settings.lSunkenFrameColor,
                        plswData->Settings.lRaisedFrameColor,DB_DEPRESSED);
        }
      }

    }
    WinEndPaint(hps);
    break;
  }

  case LSWM_WNDSTATECHANGED:
  case LSWM_WNDICONCHANGED: {
    USHORT k,usPosX,usPosY;
    RECTL rcl;
    UCHAR ucBuf[150];
    BOOL bNeedUpdate;

    if (!plswData->bNowActive) break;

    for (k=0;k<plswData->usItems;k++)
      if (plswData->TaskArr[k].hwnd==HWNDFROMMP(mp2)) {
        if (msg==LSWM_WNDICONCHANGED) {
          plswData->TaskArr[k].hIcon=LONGFROMMP(mp1);
          WinSendMsg(HWNDFROMMP(mp2),WM_NULL,0,0); // to make sure the icon handle has become valid
          bNeedUpdate = TRUE;
        } else {
          bNeedUpdate = UpdateWinFlags(&plswData->TaskArr[k].fl, SHORT2FROMMP(mp1));

          if (plswData->TaskArr[MAXITEMS-1].hwnd == plswData->TaskArr[k].hwnd)
            plswData->TaskArr[MAXITEMS-1].fl = plswData->TaskArr[k].fl;

          if (k==plswData->usCurrItem && bNeedUpdate) {
            MakeHintStr(plswData,k,ucBuf,sizeof(ucBuf)); //change hints to reflect the changed task state
            WinSetWindowText(hwndHints,ucBuf);
          }
        }

        if (bNeedUpdate) {
          IconPosFromNumber(plswData, IconNumFromItemNum(plswData,k), &usPosX,&usPosY);
          WinSetRect(plswData->hab,&rcl,usPosX,usPosY,usPosX+cxIcon,usPosY+cyIcon);
          WinInvalidateRect(hwnd,&rcl,FALSE);
        }

        break;
      }
    break;
  }
  case LSWM_SWITCHLISTCHANGED: {
    USHORT k;
    UCHAR ucBuf[NAMELEN];

    if (!plswData->bNowActive) break;

    for (k=0;k<plswData->usItems;k++) {
      if (plswData->TaskArr[k].hsw!=LONGFROMMP(mp2)) continue;

      if (LONGFROMMP(mp1)==2) ShowBubble(plswData,k,0,0,11); //update bubble

      if (LONGFROMMP(mp1)==2 && k==plswData->usCurrItem) {
        GetItemTitle(plswData->TaskArr[k].hsw,ucBuf,sizeof(ucBuf),TRUE);
        WinSetWindowText(hwndTitle,ucBuf);
      }
      if ((LONGFROMMP(mp1)==2 &&
           (GetItemTitle(plswData->TaskArr[k].hsw,ucBuf,sizeof(ucBuf),FALSE),
            IsInSkipList(&plswData->Settings,ucBuf,FALSE))) ||
          LONGFROMMP(mp1)==3) {
        memmove(&plswData->TaskArr[k],&plswData->TaskArr[k+1],sizeof(SWITEM)*(plswData->usItems-k-1));
        plswData->usItems--;
        if (plswData->usItems==0)
          WinPostMsg(plswData->hwndPopup,WM_COMMAND,MPFROMSHORT(CMD_CANCELPOPUP1),
                     MPFROM2SHORT(CMDSRC_OTHER,FALSE));
        else if (WinQueryFocus(HWND_DESKTOP)==plswData->hwndPopClient && !plswData->Settings.bOldPopup)
          AdjustPopupWin(plswData,FALSE);

        break;
      }
    }
    break;

  }
/*
  case WM_PRESPARAMCHANGED: {
    LONG lParam, lValue;
    UCHAR ucBuf[4];

    lParam = LONGFROMMP(mp1);

    if (lParam==PP_BACKGROUNDCOLOR &&
        WinQueryPresParam(hwnd,lParam,0,NULL,sizeof(ucBuf),ucBuf,QPF_NOINHERIT)!=0) {
      lValue = ucBuf[0]+ucBuf[1]*256+ucBuf[2]*65536;
//      plswData->Settings.lBackCol = lValue;
      WinSetPresParam(hwndTitle,PP_BACKGROUNDCOLOR,4,&lValue);
      WinSetPresParam(hwndHints,PP_BACKGROUNDCOLOR,4,&lValue);
      WinInvalidateRect(hwnd,NULL,TRUE);
    }
    break;
  }
*/
  case WM_TIMER: {
    static SHORT iMouseIsAtItem;
    POINTL ptl;
    SWP swp1,swp2;
    HWND hwndFocus;

    if (plswData->bNowActive) {
      if (WinQueryFocus(HWND_DESKTOP) == plswData->hwndPopClient &&
          WinQueryWindow(HWND_DESKTOP,QW_TOP) != plswData->hwndPopup)
        WinSetWindowPos(plswData->hwndPopup,WinIsWindowVisible(plswData->hwndBubble)?
                        plswData->hwndBubble:HWND_TOP,0,0,0,0,SWP_ZORDER);

      hwndFocus = WinQueryFocus(HWND_DESKTOP);
      if (hwndFocus != plswData->hwndPopClient && hwndFocus != plswData->hwndMenu &&
          WinQueryWindow(hwndFocus,QW_OWNER) != plswData->hwndMenu)
        WinPostMsg(plswData->hwndPopup,WM_COMMAND,MPFROMSHORT(CMD_CANCELPOPUP1),//cancel poup and don't return to original session
                   MPFROM2SHORT(CMDSRC_OTHER,FALSE));
      else if (!(plswData->Settings.bStickyPopup||bNowSticky) &&
               (WinGetPhysKeyState(HWND_DESKTOP,(plswData->Settings.bUseCtrl ?
                                                 SCAN_CTRL : SCAN_ALT)) & 0x8000)==0)
        WinPostMsg(plswData->hwndPopup,WM_COMMAND,MPFROMSHORT(CMD_SWITCHFROMPM),
                   MPFROM2SHORT(CMDSRC_OTHER,FALSE));
    }

/* check if mouse pointer is inside the popup window and above an icon */
    if (plswData->bNowActive && (WinQueryPointerPos(HWND_DESKTOP,&ptl),
        WinQueryWindowPos(plswData->hwndPopup,&swp1),WinQueryWindowPos(hwnd,&swp2),
        (ptl.x>=swp1.x+swp2.x && ptl.y>=swp1.y+swp2.y &&
        ptl.x<=swp1.x+swp2.x+swp2.cx && ptl.y<=swp1.y+swp2.y+swp2.cy)) &&
        (iMouseIsAtItem=
        ItemNumFromPos(plswData,ptl.x-swp1.x-swp2.x,ptl.y-swp1.y-swp2.y))>=0 &&
        !WinIsWindowVisible(plswData->hwndMenu))

      ShowBubble(plswData,iMouseIsAtItem,ptl.x,ptl.y-cyPointer-1,1);
    else
      ShowBubble(plswData,0,0,0,-1);

    break;
  }
  default:
    mrc = WinDefWindowProc(hwnd, msg, mp1, mp2);
  }
  return mrc;
}


LONG SwitcherInit(HAB hab, HMQ hmq, UCHAR *ucErrMsg, USHORT usMsgLen, PVOID *ppData, USHORT usFunc)
{ ULONG flFrameFlags,ulHookVer;
  LONG rc,rc1 = 0;
  LSWDATA *plswData;
  UCHAR ucFName[CCHMAXPATH], ucResLang,ucFound=0,ucLangStr[32];
  SWCNTRL swctl;

// usFunc is 0 for app initialization, 1 and for widget phases 1 and 2 initialization
  if (usFunc != 1)
    DosSetPriority(PRTYS_THREAD,PRTYC_TIMECRITICAL,+31,0);

  if (usFunc !=2) {
//need to loop so that search handle is closed
    while (FindResDll(ucFName, sizeof(ucFName), &ucResLang, ucLangStr, sizeof(ucLangStr)))
      if (ucResLang!=0) ucFound=1;
    if (!ucFound) {
      strncpy(ucErrMsg,"Could not find resource DLL or resource DLL invalid",usMsgLen);
      return 1;
    }

    ulHookVer = lswHookGetVersion();
    if (LOUCHAR(LOUSHORT(ulHookVer)) < LASTHOOKVERMAJOROK ||
                HIUCHAR(LOUSHORT(ulHookVer)) < LASTHOOKVERMINOROK ||
                LOUCHAR(HIUSHORT(ulHookVer)) < LASTHOOKREVISIONOK) {
      strncpy(ucErrMsg,"Wrong hook DLL version",usMsgLen);
      return 1;
    }

    if (!WinRegisterClass(hab,LSWPOPUPCLASS,PopupWndProc,CS_SAVEBITS|CS_SYNCPAINT,sizeof(PLSWDATA))) {
      strncpy(ucErrMsg,"WinRegisterClass(lswPopupClass)",usMsgLen);
      return WinGetLastError(hab);
    }

    if ((rc = DosAllocSharedMem((VOID*)&plswData,SHAREMEM_NAME,sizeof(LSWDATA),PAG_COMMIT | PAG_READ | PAG_WRITE))!=0) {
      strncpy(ucErrMsg,"DosAllocSharedMem",usMsgLen);
      return rc;
    }
    memset(plswData,0,sizeof(LSWDATA));
    *ppData = (PVOID)plswData;

#ifdef XWORKPLACE
    plswData->bWidget = TRUE;
#endif

    plswData->hab = hab;
    plswData->hmq = hmq;

    GetIniFileName(ucFName,sizeof(ucFName));
    if (LoadSettings(hab,ucFName,&plswData->Settings)!=0) rc1=LSWERRCANTLOADSETTINGS;
    if (rc1==0 && !CheckSettings(&plswData->Settings)) rc1=LSWERROLDSETTINGS;

    if (rc1 < 0) InitSettings(&plswData->Settings);
  }

  if (usFunc != 1) {
    if (usFunc==2)
      plswData=(PLSWDATA)(*ppData);

    if ((plswData->hmodRes=LoadResource(plswData->Settings.ucLanguage, plswData, TRUE))==0) {
      strncpy(ucErrMsg,"Could not load resources ",usMsgLen);
      return 1;
    }

    if ((rc = DosCreateEventSem(SEMRUNNINGNAME,&plswData->hevRunning,0,0)) ||
        (rc = DosCreateEventSem(SEMCTRLTABNAME,&plswData->hevCtrlTab,0,plswData->Settings.bUseCtrl))) {
      strncpy(ucErrMsg,"DosCreateEventSem",usMsgLen);
      return rc;
    }

    flFrameFlags = FCF_DLGBORDER;
#ifndef XWORKPLACE
    flFrameFlags |= FCF_TASKLIST | FCF_ICON;
#endif
    plswData->hwndPopup = WinCreateStdWindow(HWND_DESKTOP,0,&flFrameFlags,
                          LSWPOPUPCLASS,"",
                          0,0,ID_POPUPWIN,&plswData->hwndPopClient);

    if (plswData->hwndPopup==NULLHANDLE) {
      strncpy(ucErrMsg,"WinCreateStdWin(lswPopupWin)",usMsgLen);
      return WinGetLastError(hab);
    }

    WinSetWindowPos(plswData->hwndPopup,HWND_BOTTOM, POPUPWINHIDEPOSX, POPUPWINHIDEPOSY,
                    0, 0, SWP_MOVE | SWP_SIZE | SWP_SHOW | SWP_DEACTIVATE | SWP_ZORDER);

    WinSetWindowPtr(plswData->hwndPopup, 0, (VOID *)WinSubclassWindow(plswData->hwndPopup, FrameWndProc));

    if ((plswData->itidFSDispat = _beginthread(FSMonDispat,NULL,0x4000,plswData))<0) {
      strncpy(ucErrMsg,"_beginthread",usMsgLen);
      return plswData->itidFSDispat;
    }

    if ((rc = lswHookInit(plswData)) < 0) {
      strncpy(ucErrMsg,"lswHookInit",usMsgLen);
      return (-rc);
    } 

#ifndef XWORKPLACE
    plswData->hswitch = WinQuerySwitchHandle(plswData->hwndPopup, 0);
    WinQuerySwitchEntry(plswData->hswitch, &swctl);
    swctl.uchVisibility = plswData->Settings.bShowInWinList ? SWL_VISIBLE : SWL_INVISIBLE;
    WinChangeSwitchEntry(plswData->hswitch, &swctl);

    if (plswData->Settings.bTaskBarOn)
      if ((rc = InitTaskBar(plswData,ucErrMsg,usMsgLen)) != 0)
        return rc;

    if ((rc = DosExitList(EXLST_ADD, (PFNEXITLIST)lswExitProc)) !=0 ) {
      strncpy(ucErrMsg,"DosExitList",usMsgLen);
      return rc;
    }
#else
    if (plswData->pWidget->pGlobals!=NULL) plswData->hswitch = WinQuerySwitchHandle(plswData->pWidget->pGlobals->hwndFrame, 0);
#endif
  }

  return rc1;
}


VOID SwitcherTerm(LSWDATA *plswData,USHORT usFunc)
{
// usFunc is 0 for app termination, 1 and for widget phases 1 and 2 termination

  if (usFunc!=2) {
    lswHookTerm(plswData);

    DosPostEventSem(plswData->hevRunning);
    DosSleep(500);

#ifndef XWORKPLACE
    DoneTaskBar(plswData);
#endif

    DosCloseEventSem(plswData->hevShift);
    DosCloseEventSem(plswData->hevPopup);
    DosCloseEventSem(plswData->hevRunning);
    DosCloseEventSem(plswData->hevCtrlTab);

    WinDestroyWindow(plswData->hwndPopup);
    if (plswData->bSettingsDlgOn) WinDestroyWindow(plswData->hwndParamDlg);

    DosFreeModule(plswData->hmodRes);
  }

#ifndef XWORKPLACE
  WinDestroyMsgQueue(plswData->hmq);
  WinTerminate(plswData->hab);
#endif

  if (usFunc!=1)
    DosFreeMem(plswData);
}


VOID APIENTRY lswExitProc()
{ LSWDATA *plswData;

  DosGetNamedSharedMem((PVOID*)&plswData,SHAREMEM_NAME,PAG_READ | PAG_WRITE);

  if (plswData!=NULL) SwitcherTerm(plswData,0);

  DosExitList(EXLST_EXIT, (PFNEXITLIST)lswExitProc);
}

