/*
 *      Copyright (C) 1997-2006 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 "fspopup.h"
#include "prmdlg.h"
#include "pmpopup.h"
#include "lswres.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <ctype.h>
#ifndef __WATCOMC__
#include <unikbd.h>
#endif

/* we subclass the window procedure of the popup window frame */
MRESULT EXPENTRY FrameWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    LSWDATA *plswData;

    if (msg != WM_CREATE)
#ifndef XWORKPLACE
	plswData = WinQueryWindowPtr(WinWindowFromID(hwnd, FID_CLIENT), 0);
#else
	plswData =
	    ((PXCENTERWIDGET)
	     WinQueryWindowPtr(WinWindowFromID(hwnd, FID_CLIENT), 0))->pUser;
#endif

    switch (msg) {
    case WM_WINDOWPOSCHANGED:{
	    PSWP pswp = ((PSWP) PVOIDFROMMP(mp1));
	    SHORT x, y, oldx = (plswData->lCurrDesktop & 0xFF00) >> 8, oldy =
		plswData->lCurrDesktop & 0xFF;
	    USHORT usCXScreen =
		WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN), usCYScreen =
		WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);

	    if (plswData->bNowActive || !(pswp->fl & (SWP_SIZE | SWP_MOVE)))
		break;

	    x = (POPUPWINHIDEPOSX - pswp->x) / usCXScreen;
	    y = (POPUPWINHIDEPOSY - pswp->y) / usCYScreen;
	    plswData->lCurrDesktop = ((x << 8) + y);

	    if (x != oldx || y != oldy) {
		SWP swp;
		HWND hwndNext;
		USHORT k;
		BOOL bMinMax;
		HENUM henum = WinBeginEnumWindows(HWND_DESKTOP);

		while ((hwndNext = WinGetNextWindow(henum)) != NULLHANDLE) {
		    WinQueryWindowPos(hwndNext, &swp);
		    if (swp.fl & SWP_MINIMIZE) {
			for (k = 0, bMinMax = FALSE;
			     k < MINMAXITEMS && !bMinMax; k++)
			    if (plswData->MinMaxWnd[2 * k] == hwndNext)
				bMinMax = TRUE;
			if (!bMinMax) {
			    swp.x =
				WinQueryWindowUShort(hwndNext, QWS_XRESTORE);
			    swp.y =
				WinQueryWindowUShort(hwndNext, QWS_YRESTORE);
			    swp.x += (oldx - x) * (usCXScreen + 8);
			    swp.y += (oldy - y) * (usCYScreen + 8);
			    WinSetWindowUShort(hwndNext, QWS_XRESTORE, swp.x);
			    WinSetWindowUShort(hwndNext, QWS_YRESTORE, swp.y);
			}
		    }
		    else if (swp.fl & SWP_HIDE) {
			swp.x += (oldx - x) * (usCXScreen + 8);
			swp.y += (oldy - y) * (usCYScreen + 8);
			WinSetWindowPos(hwndNext, 0, swp.x, swp.y, 0, 0,
					SWP_MOVE);
		    }
		}
		WinEndEnumWindows(henum);
	    }

	    break;
	}

    case WM_ADJUSTWINDOWPOS:{
	    PSWP pswp = ((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));
	    }
	    break;
	}

    }
    return ((PFNWP) WinQueryWindowPtr(hwnd, 0)) (hwnd, msg, mp1, mp2);
}

/* 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 - (rcl.xRight -
						   rcl.xLeft) /
	(POPUPBORDERSIZER + 1);
}

/* 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), FALSE)
	    && k != CMD_MOVE) {
	    if ((pucTilde = strchr(ucBuf, '~')) != NULL) {
		memmove(pucTilde, pucTilde + 1, strlen(pucTilde));
		if (plswData->Settings.usLanguage == RU) {
		    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.usLanguage == RU) {
		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.usLanguage != DE)
		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, usPopupBorderSize;
    RECTL rcl;
    HWND hwndTitle, hwndHints;
    HPS hps;
    POINTL aptl[TXTBOX_COUNT], ptl;
    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_BACKGROUNDCOLOR,
			sizeof(plswData->Settings.lBackColor),
			&plswData->Settings.lBackColor);
	WinSetPresParam(hwndTitle, PP_FOREGROUNDCOLOR,
			sizeof(plswData->Settings.lTitleRGBCol),
			&plswData->Settings.lTitleRGBCol);

	WinSetPresParam(hwndHints, PP_BACKGROUNDCOLOR,
			sizeof(plswData->Settings.lBackColor),
			&plswData->Settings.lBackColor);
	WinSetPresParam(hwndHints, PP_FOREGROUNDCOLOR,
			sizeof(plswData->Settings.lHintsRGBCol),
			&plswData->Settings.lHintsRGBCol);

	WinSetAccelTable(plswData->hab,
			 plswData->Settings.bStickyPopup ? plswData->
			 haccNoAlt : plswData->Settings.ucPopupHotKey ==
			 0 ? plswData->haccAlt : plswData->Settings.
			 ucPopupHotKey ==
			 1 ? plswData->haccCtrl : plswData->haccNoAlt,
			 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), FALSE);
    WinSetWindowText(hwndTitle, ucBuf);

    MakeHintStr(plswData, plswData->usCurrItem, ucBuf, sizeof(ucBuf));
    WinSetWindowText(hwndHints, ucBuf);

    WinSendMsg(plswData->hwndPopup, WM_QUERYBORDERSIZE, MPFROMP(&ptl), 0);
    cxPopup =
	cxIcon * MAXICONSINLINE + cxIcon * (MAXICONSINLINE +
					    2) * ICONSPACINGX + 2 * ptl.x;
    usPopupBorderSize = cxPopup / POPUPBORDERSIZER;
    cxPopup += 2 * usPopupBorderSize;
    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 * ptl.y +
	2 * usPopupBorderSize;

    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 if we set the focus explicitly
    if (bInit) {
	WinSetFocus(HWND_DESKTOP, plswData->hwndPopClient);
	WinStartTimer(plswData->hab, plswData->hwndPopClient, 0,
		      BUBBLETIMERINTERVAL);
    }

    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 + usPopupBorderSize,
			1 + usPopupBorderSize +
			(plswData->Settings.bShowHints ? usTxtHgtHints : 0),
			rcl.xRight - 2 - 2 * usPopupBorderSize,
			usTxtHgtTitle *
			(plswData->Settings.bShowHints ? 2 : 3) / 2,
			SWP_SIZE | SWP_MOVE);
	WinSetWindowPos(hwndHints, 0,
			plswData->Settings.bShowHints ? 1 +
			usPopupBorderSize : 0,
			plswData->Settings.bShowHints ? 1 +
			usPopupBorderSize : 0,
			plswData->Settings.bShowHints ? rcl.xRight - 2 -
			2 * usPopupBorderSize : 0,
			plswData->Settings.bShowHints ? usTxtHgtHints : 0,
			SWP_SIZE | SWP_MOVE);
    }

    WinInvalidateRect(plswData->hwndPopClient, NULL, TRUE);
}

/* popup client window procedure */
MRESULT EXPENTRY PopupWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    static ULONG cxIcon, cyIcon, cyPointer;
    static HWND hwndTitle, hwndHints;
    static BOOL bNowSticky = FALSE;
    static USHORT usScrollCnt = 0;
    LSWDATA *plswData;
    MRESULT mrc = 0;

    if (msg != WM_CREATE)
#ifndef XWORKPLACE
	plswData = WinQueryWindowPtr(hwnd, 0);
#else
	plswData = ((PXCENTERWIDGET) WinQueryWindowPtr(hwnd, 0))->pUser;
#endif

    switch (msg) {
    case WM_HSCROLL:
    case WM_VSCROLL:
	usScrollCnt = ++usScrollCnt % plswData->Settings.usScrollDiv;

	if (usScrollCnt == 0
	    && (SHORT2FROMMP(mp2) == SB_LINELEFT
		|| SHORT2FROMMP(mp2) == SB_LINERIGHT))
	    WinPostMsg(hwnd, WM_COMMAND,
		       MPFROMSHORT(SHORT2FROMMP(mp2) ==
				   SB_LINELEFT ? CMD_SCROLLLEFT :
				   CMD_SCROLLRIGHT),
		       MPFROM2SHORT(CMDSRC_OTHER, FALSE));
	break;

    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);
		ProcessCommand(CMD_SWITCHFROMPM, CMDSRC_OTHER, plswData,
			       FALSE, FALSE);
		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, FALSE);
	    ShowBubble(plswData, 0, 0, 0, -1, NULL);

	    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 ((plswData->Settings.ucPopupHotKey == 0
		     && (ucScan == SCAN_CTRL || ucScan == SCAN_RIGHTCTRL))
		    || (plswData->Settings.ucPopupHotKey == 1
			&& (ucScan == SCAN_ALT || ucScan == SCAN_RIGHTALT))
		    || (plswData->Settings.ucPopupHotKey == 2
			&& (ucScan == SCAN_CTRL || ucScan == SCAN_RIGHTCTRL
			    || ucScan == SCAN_ALT
			    || ucScan == SCAN_RIGHTALT))) {
		    bNowSticky = TRUE;
		    WinSetAccelTable(plswData->hab, plswData->haccNoAlt,
				     plswData->hwndPopup);
		    break;
		}

		// left alt, ctrl or winkey release, switch to the selected window
		if (ucScan ==
		    ((plswData->Settings.bStickyPopup
		      || bNowSticky) ? SCAN_ENTER : plswData->Settings.
		     ucPopupHotKey ==
		     0 ? SCAN_ALT : plswData->Settings.ucPopupHotKey ==
		     1 ? SCAN_CTRL : SCAN_WINLEFT))
		    ProcessCommand(CMD_SWITCHFROMPM, CMDSRC_OTHER, plswData,
				   FALSE, 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, TRUE);
		    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 / max((SHORT) plswData->usItems,
					       1) * 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), FALSE);
		    WinSetWindowText(hwndTitle, ucBuf);

		    MakeHintStr(plswData, plswData->usCurrItem, ucBuf,
				sizeof(ucBuf));
		    WinSetWindowText(hwndHints, ucBuf);
		}
		break;
	    }

	default:
	    if (plswData->bNowActive &&
		(SHORT1FROMMP(mp1) == CMD_SWITCHFROMPM
		 || SHORT1FROMMP(mp1) == CMD_CANCELPOPUP
		 || SHORT1FROMMP(mp1) == CMD_CANCELPOPUP1)) {
		UCHAR ucBuf[128];

		usScrollCnt = 0;
		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));
		WinQueryPresParam(hwndTitle, PP_FOREGROUNDCOLOR, 0, NULL,
				  sizeof(ucBuf), ucBuf, QPF_NOINHERIT);
		plswData->Settings.lTitleRGBCol =
		    ucBuf[0] + ucBuf[1] * 256 + ucBuf[2] * 65536;
		WinQueryPresParam(hwndHints, PP_FOREGROUNDCOLOR, 0, NULL,
				  sizeof(ucBuf), ucBuf, QPF_NOINHERIT);
		plswData->Settings.lHintsRGBCol =
		    ucBuf[0] + ucBuf[1] * 256 + ucBuf[2] * 65536;
	    }
	    ProcessCommand(SHORT1FROMMP(mp1), SHORT1FROMMP(mp2), plswData,
			   FALSE, FALSE);
	}

	break;

    case WM_CREATE:{
	    DosGetNamedSharedMem((PVOID *) & plswData, SHAREMEM_NAME,
				 PAG_READ | PAG_WRITE);

	    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);

#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_MENUEND:
	WinSetWindowPos(plswData->hwndMenu, HWND_BOTTOM, 0, 0, 0, 0,
			SWP_ZORDER);
	if (plswData->bNowActive)
	    WinSetFocus(HWND_DESKTOP, hwnd);
	break;

    case WM_ERASEBACKGROUND:
	mrc = (MRESULT) FALSE;
	break;

    case WM_PAINT:{
	    HPS hps;
	    RECTL rclUpdate, rclWnd, rclSelBox;
	    USHORT k, usPosX, usPosY, usItemNum, usBorderSize, usR, usB, usG;

	    hps = WinBeginPaint(hwnd, NULLHANDLE, &rclUpdate);
	    WinQueryWindowRect(hwnd, &rclWnd);
	    usBorderSize =
		max(1,
		    (rclWnd.xRight - rclWnd.xLeft) / (POPUPBORDERSIZER + 1));

	    GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);

	    WinFillRect(hps, &rclUpdate, plswData->Settings.lBackColor);

	    usR = (plswData->Settings.lBackColor & 0xFF0000) >> 16,
		usG = (plswData->Settings.lBackColor & 0xFF00) >> 8,
		usB = (plswData->Settings.lBackColor & 0xFF);

	    for (k = 1; k <= usBorderSize; k++) {
		ULONG ulColor;

		ulColor =
		    (230 - usB) / usBorderSize * ( /*usBorderSize- */ k) +
		    usB +
		    ((230 - usG) / usBorderSize * ( /*usBorderSize- */ k) +
		     usG) * 256 +
		    ((230 - usR) / usBorderSize * ( /*usBorderSize- */ k) +
		     usR) * 65536;
		WinDrawBorder(hps, &rclWnd, 1, 1, ulColor, ulColor, 0);
		rclWnd.xLeft += 1;
		rclWnd.xRight -= 1;
		rclWnd.yBottom += 1;
		rclWnd.yTop -= 1;
	    }

	    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 WM_USER+101: {
    HPS hpsScreen;
    USHORT usShadowSize;
    POINTL ptl;
    SWP swp;

    if (msg==WM_USER+101)
      WinSendMsg(HWNDFROMMP(mp1),WM_NULL,0,0);
    WinQueryWindowPos(plswData->hwndPopup,&swp);
    hpsScreen=WinGetScreenPS(HWND_DESKTOP);
    GpiCreateLogColorTable(hpsScreen, 0, LCOLF_RGB, 0, 0, NULL);
    usShadowSize=swp.cx/(SHADOWSIZER+1);
    GpiSetColor(hpsScreen,0x00555555);
    GpiSetMix(hpsScreen,FM_AND);
    ptl.x=swp.x+swp.cx; ptl.y=swp.y;
    GpiSetCurrentPosition(hpsScreen,&ptl);
    ptl.x+=usShadowSize; ptl.y+=swp.cy-usShadowSize;
    GpiBox(hpsScreen,DRO_FILL,&ptl,0,0);
    WinReleasePS(hpsScreen);
    break;
  }
*/
    case LSWM_WNDSTATECHANGED:
//clear the list of minimized/hidden windows (reset the function)
	if (SHORT2FROMMP(mp1) & (SWP_RESTORE | SWP_MAXIMIZE | SWP_SHOW))
	    MinimizeHideAll(plswData, TRUE, HWNDFROMMP(mp2));

    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 (IsWindowClass(WinWindowFromID(HWNDFROMMP(mp2), FID_CLIENT), "SeamlessClass")) {	//dynamic WIn-OS2 icon handles change
			    DosSleep(20);	//there is some delay in icon change
			    plswData->TaskArr[k].hIcon =
				GetWndIcon(HWNDFROMMP(mp2));
			    bNeedUpdate = TRUE;
			}
		    }

		    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, NULL);	//update bubble

		if (LONGFROMMP(mp1) == 2 && k == plswData->usCurrItem) {
		    GetItemTitle(plswData->TaskArr[k].hsw, ucBuf,
				 sizeof(ucBuf), FALSE);
		    WinSetWindowText(hwndTitle, ucBuf);
		}
		if ((LONGFROMMP(mp1) == 2 &&
		     (GetItemTitle
		      (plswData->TaskArr[k].hsw, ucBuf, sizeof(ucBuf), FALSE),
		      IsInSkipList(&plswData->Settings.SkipListPopup, ucBuf)))
		    || LONGFROMMP(mp1) == 3) {
		    memmove(&plswData->TaskArr[k], &plswData->TaskArr[k + 1],
			    sizeof(SWITEM) * (plswData->usItems - k - 1));
		    plswData->usItems--;
		    if (plswData->usItems == 0)
			ProcessCommand(CMD_CANCELPOPUP1, CMDSRC_OTHER, plswData, FALSE, FALSE);	//cancel popup and don't return to original session
		    else if (WinQueryFocus(HWND_DESKTOP) ==
			     plswData->hwndPopClient
			     && !plswData->Settings.bOldPopup)
			AdjustPopupWin(plswData, FALSE);

		    break;
		}
	    }
	    break;

	}

    case WM_PRESPARAMCHANGED:{
	    LONG lParam, lValue;
	    UCHAR ucBuf[64];

	    lParam = LONGFROMMP(mp1);

	    if (!
		(lParam == PP_FOREGROUNDCOLOR || lParam == PP_BACKGROUNDCOLOR)
		|| (LONG) WinQueryPresParam(hwnd, lParam, 0, NULL,
					    sizeof(ucBuf), ucBuf,
					    QPF_NOINHERIT) == 0)
		break;

	    lValue = ucBuf[0] + ucBuf[1] * 256 + ucBuf[2] * 65536;

	    switch (lParam) {
	    case PP_BACKGROUNDCOLOR:
		plswData->Settings.lBackColor =
		    plswData->Settings.lTitleRGBCol =
		    plswData->Settings.lHintsRGBCol = lValue;
		WinSetPresParam(hwndTitle, PP_BACKGROUNDCOLOR, 4, &lValue);
		WinSetPresParam(hwndHints, PP_BACKGROUNDCOLOR, 4, &lValue);
		WinInvalidateRect(hwnd, NULL, TRUE);
		break;
	    }
	    break;
	}

    case WM_TIMER:{
	    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)
		    ProcessCommand(CMD_CANCELPOPUP1, CMDSRC_OTHER, plswData, FALSE, FALSE);	//cancel popup and don't return to original session
		else if (!(plswData->Settings.bStickyPopup || bNowSticky) &&
			 (WinGetPhysKeyState
			  (HWND_DESKTOP,
			   (plswData->Settings.ucPopupHotKey ==
			    0 ? SCAN_ALT : plswData->Settings.ucPopupHotKey ==
			    1 ? SCAN_CTRL : SCAN_WINLEFT)) & 0x8000) == 0)
		    ProcessCommand(CMD_SWITCHFROMPM, CMDSRC_OTHER, plswData,
				   FALSE, 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, NULL);
	    else
		ShowBubble(plswData, 0, 0, 0, -1, NULL);

	    break;
	}
    default:
	mrc = WinDefWindowProc(hwnd, msg, mp1, mp2);
    }
    return mrc;
}
