text: #include "progman.h"
  #include "ime.h"
  
  BOOL FAR PASCAL IMEStringWindow(HWND,HANDLE);
  int FAR PASCAL IMEWindowGetCnt(LPTSTR,LPTSTR);
  
  #define HK_SHIFT    0x0100
  #define HK_CONTROL  0x0200
  #define HK_ALT      0x0400
  #define HK_EXT      0x0800
  
  #define F_EXT       0x01000000L
  
  #define OBJ_ITEM                1
  
  
  BOOL bNoScrollCalc = FALSE;
  RECT rcArrangeRect;
  HWND hwndScrolling = NULL;
  
  BOOL APIENTRY InQuotes(LPTSTR sz);
  void NEAR PASCAL ViewActiveItem(PGROUP pGroup);
  
  /* Make the first item in a list the last and return a pointer to it.*/
  
  PITEM PASCAL MakeFirstItemLast(PGROUP pGroup)
  {
      PITEM pItemCur;
  
      /* Just quit if there's no list.*/
  
      if ((pItemCur = pGroup->pItems) == NULL)
          return NULL;
  
      /* Find the end of the list.*/
      for( ; pItemCur->pNext ; pItemCur = pItemCur->pNext)
      ;
  
      /* End of the list.*/
      /* This works even if there is only one item in the */
      /* list, it's a waste of time though.*/
      pItemCur->pNext = pGroup->pItems;
      pGroup->pItems = pGroup->pItems->pNext;
      pItemCur->pNext->pNext = NULL;
  
      return pItemCur->pNext;
  }
  
  
  VOID PASCAL GetItemText(PGROUP pGroup, PITEM pItem, LPTSTR lpT, int index)
  {
      LPITEMDEF lpid;
      LPGROUPDEF lpgd;
  
      lpid = LockItem(pGroup,pItem);
      if (!lpid) {
          UnlockGroup(pGroup->hwnd);
          *lpT = 0;
          return;
      }
  
      lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
  
      switch (index) {
  
      case 0:
          lstrcpy(lpT, (LPTSTR) PTR(lpgd, lpid->pName));
          break;
  
      case 1:
          lstrcpy(lpT, (LPTSTR) PTR(lpgd, lpid->pCommand));
          break;
  
      case 2:
          lstrcpy(lpT, (LPTSTR) PTR(lpgd, lpid->pIconPath));
          break;
  
      default:
          *lpT = 0;
          break;
      }
  
      GlobalUnlock(pGroup->hGroup);
  
      UnlockGroup(pGroup->hwnd);
  }
  
  
  ULONG Color16Palette[] = {
      0x00000000, 0x00800000, 0x00008000, 0x00808000,
      0x00000080, 0x00800080, 0x00008080, 0x00808080,
      0x00c0c0c0, 0x00ff0000, 0x0000ff00, 0x00ffff00,
      0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
  };
  
  ULONG Color256Palette[] = {
      0x00000000, 0x00800000, 0x00008000, 0x00808000, 0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0,
      0x00c0dcc0, 0x00a6caf0, 0x00cccccc, 0x00580800, 0x00600800, 0x00680800, 0x00700800, 0x00780800,
      0x00801000, 0x00881000, 0x00901000, 0x00981000, 0x00a01000, 0x00a81000, 0x00b01000, 0x00b81000,
      0x00c01800, 0x00c81800, 0x00d01800, 0x00d81800, 0x00e01800, 0x00e81800, 0x00f01800, 0x00f81800,
      0x00002000, 0x00082000, 0x00102000, 0x00182000, 0x00202000, 0x00282000, 0x00302000, 0x00382000,
      0x00402800, 0x00482800, 0x00502800, 0x00582800, 0x00602800, 0x00682800, 0x00702800, 0x00782800,
      0x00803000, 0x00883000, 0x00903000, 0x00983000, 0x00a03000, 0x00a83000, 0x00b03000, 0x00b83000,
      0x00c03800, 0x00c83800, 0x00d03800, 0x00d83800, 0x00e03800, 0x00e83800, 0x00f03800, 0x00f83800,
      0x00004010, 0x00084010, 0x00104010, 0x00184010, 0x00204010, 0x00284010, 0x00304010, 0x00384010,
      0x00404810, 0x00484810, 0x00504810, 0x00584810, 0x00604810, 0x00684810, 0x00704810, 0x00784810,
      0x00805010, 0x00885010, 0x00905010, 0x00985010, 0x00a05010, 0x00a85010, 0x00b05010, 0x00b85010,
      0x00c05810, 0x00c85810, 0x00d05810, 0x00d85810, 0x00e05810, 0x00e85810, 0x00f05810, 0x00f85810,
      0x00006010, 0x00086010, 0x00106010, 0x00186010, 0x00206010, 0x00286010, 0x00306010, 0x00386010,
      0x00406810, 0x00486810, 0x00506810, 0x00586810, 0x00606810, 0x00686810, 0x00706810, 0x00786810,
      0x00807010, 0x00887010, 0x00907010, 0x00987010, 0x00a07010, 0x00a87010, 0x00b07010, 0x00b87010,
      0x00c07810, 0x00c87810, 0x00d07810, 0x00d87810, 0x00e07810, 0x00e87810, 0x00f07810, 0x00f87810,
      0x00008020, 0x00088020, 0x00108020, 0x00188020, 0x00208020, 0x00288020, 0x00308020, 0x00388020,
      0x00408820, 0x00488820, 0x00508820, 0x00588820, 0x00608820, 0x00688820, 0x00708820, 0x00788820,
      0x00809020, 0x00889020, 0x00909020, 0x00989020, 0x00a09020, 0x00a89020, 0x00b09020, 0x00b89020,
      0x00c09820, 0x00c89820, 0x00d09820, 0x00d89820, 0x00e09820, 0x00e89820, 0x00f09820, 0x00f89820,
      0x0000a020, 0x0008a020, 0x0010a020, 0x0018a020, 0x0020a020, 0x0028a020, 0x0030a020, 0x0038a020,
      0x0040a820, 0x0048a820, 0x0050a820, 0x0058a820, 0x0060a820, 0x0068a820, 0x0070a820, 0x0078a820,
      0x0080b020, 0x0088b020, 0x0090b020, 0x0098b020, 0x00a0b020, 0x00a8b020, 0x00b0b020, 0x00b8b020,
      0x00c0b820, 0x00b820c8, 0x00b820d0, 0x00b820d8, 0x00b820e0, 0x00b820e8, 0x00b820f0, 0x00b820f8,
      0x0000c030, 0x00c03008, 0x00c03010, 0x00c03018, 0x00c03020, 0x00c03028, 0x00c03030, 0x00c03038,
      0x0040c830, 0x00c83048, 0x00c83050, 0x00c83058, 0x00c83060, 0x00c83068, 0x00c83070, 0x00c83078,
      0x0080d030, 0x00d03088, 0x00d03090, 0x00d03098, 0x00d030a0, 0x00d030a8, 0x00d030b0, 0x00d030b8,
      0x00c0d830, 0x00c8d830, 0x00d0d830, 0x00d8d830, 0x00e0d830, 0x00e8d830, 0x00f0d830, 0x00f8d830,
      0x0000e030, 0x0008e030, 0x0010e030, 0x0018e030, 0x0020e030, 0x0028e030, 0x0030e030, 0x0038e030,
      0x0040e830, 0x0048e830, 0x0050e830, 0x0058e830, 0x0060e830, 0x0068e830, 0x0070e830, 0x0078e830,
      0x0080f030, 0x0088f030, 0x0090f030, 0x0098f030, 0x00a0f030, 0x00a8f030, 0x00fffbf0, 0x00a0a0a4,
      0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00, 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
  };
  
  
  HICON APIENTRY GetItemIcon(HWND hwnd, PITEM pitem)
  {
  
      LPGROUPDEF lpgd;
      LPITEMDEF lpid;
      HICON hIcon = NULL;
      DWORD dwVer;
      HANDLE h;
      LPBYTE p;
      INT id = 0;
      INT cb;
      DWORD colors, size;
      PBITMAPINFOHEADER pbih, pbihNew;
      LPVOID palette;
  
      lpgd = LockGroup(hwnd);
      if (!lpgd)
          return DuplicateIcon(hAppInstance, hItemIcon);
          //return hItemIcon;
      lpid = ITEM(lpgd,pitem->iItem);
  
      if ((SHORT)lpid->cbIconRes > 0) {
          if (lpid->wIconVer == 2)
              dwVer = 0x00020000;
          else
              dwVer = 0x00030000;
  
          pbihNew = NULL;
          pbih = (PBITMAPINFOHEADER)PTR(lpgd, lpid->pIconRes);
          size = lpid->cbIconRes;
  
          if (pbih->biClrUsed == -1) {
              colors = (1 << (pbih->biPlanes * pbih->biBitCount));
              size += colors * sizeof(RGBQUAD);
  
              if (colors == 16) {
                  palette = Color16Palette;
              } else if (colors == 256) {
                  palette = Color256Palette;
              } else {
                  palette = NULL;
              }
  
              if (palette != NULL)
                  pbihNew = (PBITMAPINFOHEADER)LocalAlloc(LPTR, size);
  
              if (pbihNew != NULL) {
                  RtlCopyMemory(pbihNew, pbih, sizeof( *pbih ));
                  pbihNew->biClrUsed = 0;
                  RtlCopyMemory((pbihNew+1), palette, colors * sizeof(RGBQUAD));
                  RtlCopyMemory((PCHAR)(pbihNew+1) + (colors * sizeof(RGBQUAD)),
                                (pbih+1),
                                lpid->cbIconRes - sizeof(*pbih)
                               );
  
                  pbih = pbihNew;
              }
              else {
                  //
                  // reset size
                  //
                  size = lpid->cbIconRes;
              }
          }
  
          hIcon = CreateIconFromResource((PBYTE)pbih, size, TRUE, dwVer);
          if (pbihNew != NULL)
              LocalFree(pbihNew);
      }
  
      if (!hIcon) {
        if (h = FindResource(hAppInstance, (LPTSTR) MAKEINTRESOURCE(ITEMICON), RT_GROUP_ICON)) {
            h = LoadResource(hAppInstance, h);
            p = LockResource(h);
            id = LookupIconIdFromDirectory(p, TRUE);
            UnlockResource(h);
            FreeResource(h);
        }
        if (h = FindResource(hAppInstance, (LPTSTR)  MAKEINTRESOURCE(id), (LPTSTR) MAKEINTRESOURCE(RT_ICON))) {
            cb = SizeofResource(hAppInstance, h);
            h = LoadResource(hAppInstance, h);
            p = LockResource(h);
            hIcon = CreateIconFromResource(p, cb, TRUE, 0x00030000);
            UnlockResource(h);
            FreeResource(h);
        }
  
      }
      UnlockGroup(hwnd);
      return hIcon;
  }
  
  
  COLORREF PASCAL GetTitleTextColor(VOID)
  {
      COLORREF color;
  
      color = GetSysColor(COLOR_WINDOW);
      if (((WORD)LOBYTE(LOWORD(color)) +
               (WORD)HIBYTE(LOWORD(color)) +
           (WORD)LOBYTE(HIWORD(color))) >= 3*127)
        {
          return RGB(0,0,0);
        }
      else
        {
          return RGB(255,255,255);
        }
  }
  
  #define REVERSEPAINT
  
  /*-------------------------------------------------------------------------*/
  /*-------------------------------------------------------------------------*/
  
  
  void NEAR PASCAL ReverseGroupList(PGROUP pGroup)
  {
      PITEM pitem, p1, p2;
  
      for (p1 = pGroup->pItems, p2 = p1->pNext, p1->pNext = NULL; p2; ) {
  	    pitem = p2->pNext;
  	    p2->pNext = p1;
  	    p1 = p2;
  	    p2 = pitem;
  	}
      pGroup->pItems = p1;
  }
  
  VOID PASCAL PaintGroup(HWND hwnd)
  {
      register HDC hdc;
      PGROUP pGroup;
      PITEM pitem;
      PAINTSTRUCT ps;
      LPGROUPDEF lpgd;
      LPITEMDEF lpid;
      int fontheight;
      HBRUSH hbrTitle;
      HFONT hFontT;
      RECT rcWin;
      TEXTMETRIC tm;
  
      HDC hdcMem;
      HBITMAP hbmTemp;
      HBRUSH hbr, hbrTemp;
      INT nMax;
      HICON hIcon;
  
      pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
  
      hdc = BeginPaint(hwnd, &ps);
      if (!pGroup->pItems) {
          goto Exit;
      }
  
      GetClientRect(hwnd, &rcWin);
  #ifdef WEIRDBUG
  /* DavidPe - 05/15/91
   *  For some reason RectVisible() will return FALSE in
   *  situations it shouldn't.  Since this is only a
   *  performance optimization, we can ignore the problem
   *  for now.
   */
      if (!RectVisible(hdc, &rcWin))
          goto Exit;
  #endif
  
      if (!(lpgd = LockGroup(hwnd)))
          goto Exit;
  
      hFontT = SelectObject(hdc, hFontTitle);
  
      GetTextMetrics(hdc, &tm);
  
      // ToddB: This seems like a good point, I don't see why tmExternalLeading should ever
      //      need to be considered.  The result of decreasing the FontHeight is that DrawText
      //      will be used instead of TextOut in some cases, which should be harmless but might
      //      effect the apearence of some single line icon titles.
  
  //#ifdef JAPAN
      // Why we should think about ExternalLeading though we calculate the
      // title rectange by DrawText() without DT_EXTERNALLEADING. -YN
      fontheight = tm.tmHeight;
  //#else
  //    fontheight = tm.tmHeight + tm.tmExternalLeading;
  //#endif
  
      SetBkMode(hdc, TRANSPARENT);
      SetTextColor(hdc, GetTitleTextColor());
      hbrTitle = NULL;
  
      hdcMem = CreateCompatibleDC(hdc);
      if (!hdcMem)
          goto BitmapSetupComplete;
  
      if (pGroup->hbm) {
          hbmTemp = SelectObject(hdcMem, pGroup->hbm);
          if (hbmTemp)
              goto BitmapSetupComplete;
          else
              DeleteObject(pGroup->hbm);
      }
  
      for (nMax = 1, pitem = pGroup->pItems; pitem; pitem = pitem->pNext) {
  	    if (nMax <= pitem->iItem)
  	        nMax = pitem->iItem + 1;
      }
  
      pGroup->hbm = CreateDiscardableBitmap(hdc, cxIcon*lpgd->cItems, cyIcon);
      if (!pGroup->hbm)
          goto NukeMemDC;
  
      hbmTemp = SelectObject(hdcMem, pGroup->hbm);
      if (!hbmTemp)
          goto NukeBitmap;
  
      hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
      if (!hbr)
          goto NukeBitmap;
  
      hbrTemp = SelectObject(hdcMem, hbr);
      if (!hbrTemp)
          goto NukeBrush;
  
      PatBlt(hdcMem, 0, 0, cxIcon * lpgd->cItems, cyIcon, PATCOPY);
      SelectObject(hdcMem, hbrTemp);
      DeleteObject(hbr);
  
      for (pitem = pGroup->pItems; pitem != NULL; pitem = pitem->pNext) {
          if (hIcon = GetItemIcon(hwnd, pitem)) {
              DrawIcon(hdcMem, pitem->iItem * cxIcon, 0, hIcon);
              DestroyIcon(hIcon);
          } else {
              goto DeselectAndNukeBitmap;
          }
      }
  
      goto BitmapSetupComplete;
  
  NukeBrush:
       DeleteObject(hbr);
  
  DeselectAndNukeBitmap:
      SelectObject(hdcMem, hbmTemp);
  
  NukeBitmap:
       DeleteObject(pGroup->hbm);
       pGroup->hbm = NULL;
  
  NukeMemDC:
       DeleteDC(hdcMem);
       hdcMem = NULL;
  
  BitmapSetupComplete:
  
      ReverseGroupList(pGroup); // reverse the icon list
  
      /* Paint the icons */
      for (pitem = pGroup->pItems; pitem; pitem = pitem->pNext) {
        	if (!pitem->pNext
      	      && pGroup == pCurrentGroup
  	          && hwndProgman == GetActiveWindow()) {
  	        hbrTitle = (HANDLE)1;       // Use it as a flag
      	}
      	else
      	    hbrTitle = (HANDLE)0;
  
      	lpid = ITEM(lpgd,pitem->iItem);
  
  	    if (!bMove || !hbrTitle) {
  	        if (hdcMem) {
          		BitBlt(hdc, pitem->rcIcon.left + cxOffset,
  		               pitem->rcIcon.top + cyOffset,
          		       lpgd->cxIcon, lpgd->cyIcon, hdcMem,
  		               lpgd->cxIcon*pitem->iItem, 0,
          		       SRCCOPY);
  	        }
              else {
  	    	    if (RectVisible(hdc,&pitem->rcIcon)) {
  		            if (hIcon = GetItemIcon(hwnd,pitem)) {
  		            	DrawIcon(hdc, pitem->rcIcon.left + cxOffset,
                  				 pitem->rcIcon.top + cyOffset, hIcon);
          		    }
                      else {
  		            	PatBlt(hdc,pitem->rcIcon.left + cxOffset,
              			       pitem->rcIcon.top + cyOffset,
  			                   cxIcon, cyIcon, BLACKNESS);
          		    }
  		        }
      	    }
  	    }
      }
  
      /* Paint the titles. */
      for (pitem = pGroup->pItems; pitem; pitem = pitem->pNext) {
          /* test for the active icon */
  
        	if (!pitem->pNext
      	      && pGroup == pCurrentGroup
  	          && hwndProgman == GetActiveWindow()) {
      	    SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
  	        hbrTitle = CreateSolidBrush(GetSysColor(COLOR_ACTIVECAPTION));
      	}
          else {
  	        hbrTitle = (HANDLE)0;
      	}
  
  	    lpid = ITEM(lpgd,pitem->iItem);
  
      	if (!bMove || !hbrTitle) {
  	        if (hbrTitle)
          		FillRect(hdc, &(pitem->rcTitle), hbrTitle);
  
  	        /* draw multi line titles like USER does */
  
      	    if (pitem->rcTitle.bottom - pitem->rcTitle.top < fontheight*2)
                  TextOut(hdc, pitem->rcTitle.left+cxOffset, pitem->rcTitle.top,
                          (LPTSTR) PTR(lpgd, lpid->pName), lstrlen((LPTSTR) PTR(lpgd, lpid->pName)));
              else {
                  if (RectVisible(hdc,&pitem->rcTitle)) {
  	            DrawText(hdc,
                               (LPTSTR)PTR(lpgd, lpid->pName), -1,
                               &(pitem->rcTitle),
                         	     bIconTitleWrap ?
                      	        DT_CENTER | DT_WORDBREAK | DT_NOPREFIX :
                                  DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_SINGLELINE);
  
                  }
      	    }
          }
  
  
          if (hbrTitle) {
              SetTextColor(hdc, GetTitleTextColor());
              DeleteObject(hbrTitle);
              hbrTitle = NULL;
          }
      }
  
      ReverseGroupList(pGroup);	// re-reverse the icon list
  
      if (hFontT) {
          SelectObject(hdc, hFontT);
      }
  
      if (hdcMem) {
          SelectObject(hdcMem, hbmTemp);
          DeleteDC(hdcMem);
      }
  
      UnlockGroup(hwnd);
  
  Exit:
      SetBkMode(hdc, OPAQUE);
      EndPaint(hwnd, &ps);
  
  #ifdef DEBUG
      ProfStop();
      {
      TCHAR buf[80];
      wsprintf(buf, TEXT("msec to paint group = %ld\r\n"), GetTickCount() - dwStartTime);
      OutputDebugString(buf);
      }
  #endif
  }
  
  /*-------------------------------------------------------------------------*/
  /*                                                                         */
  /* Draw the group icon.                                                    */
  /*                                                                         */
  /*-------------------------------------------------------------------------*/
  VOID DrawGroupIcon(HWND hwnd)
  {
      PAINTSTRUCT ps;
      HDC hDC;
      PGROUP pGroup;
  
      hDC = BeginPaint(hwnd, &ps);
      pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
      if (pGroup->fCommon) {
          DrawIcon(hDC, cxOffset, cyOffset, hCommonGrpIcon);
      }
      else {
          DrawIcon(hDC, cxOffset, cyOffset, hGroupIcon);
      }
      EndPaint(hwnd, &ps);
  }
  
  
  PITEM PASCAL ItemHitTest(
      PGROUP pGroup,
      POINTS pts)
  {
      PITEM pItem;
      POINT pt;
  
      pt.x = (int)pts.x;
      pt.y = (int)pts.y;
  
      for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
          if (PtInRect(&pItem->rcIcon, pt) || PtInRect(&pItem->rcTitle, pt)) {
              break;
          }
      }
  
      return pItem;
  }
  
  
  HRGN PASCAL IconExcludedRgn(PGROUP pGroup, PITEM pItem)
  {
      RECT rc;
      PITEM pItemT;
      HRGN hrgn, hrgnT;
  
      hrgn = CreateRectRgn(0,0,0,0);
  
      if (!hrgn)
          return NULL;
  
      hrgnT = CreateRectRgn(0,0,0,0);
      if (!hrgnT)
        {
          return hrgn;
        }
  
      for (pItemT = pGroup->pItems;
           pItemT && pItemT != pItem;
           pItemT = pItemT->pNext)
        {
          if (IntersectRect(&rc,&pItem->rcIcon,&pItemT->rcIcon))
            {
              SetRectRgn(hrgnT,rc.left,rc.top,rc.right,rc.bottom);
              CombineRgn(hrgn,hrgn,hrgnT,RGN_OR);
            }
          if (IntersectRect(&rc,&pItem->rcIcon,&pItemT->rcTitle))
            {
              SetRectRgn(hrgnT,rc.left,rc.top,rc.right,rc.bottom);
              CombineRgn(hrgn,hrgn,hrgnT,RGN_OR);
            }
        }
  
      DeleteObject(hrgnT);
  
      return hrgn;
  }
  
  VOID APIENTRY InvalidateIcon(PGROUP pGroup, PITEM pItem)
  {
      RECT rc;
  
      if (!pGroup || !pItem)
          return;
      UnionRect(&rc, &pItem->rcIcon, &pItem->rcTitle);
      if (bAutoArranging)
          UnionRect(&rcArrangeRect, &rcArrangeRect, &rc);
      else
          InvalidateRect(pGroup->hwnd,&rc,TRUE);
  }
  
  VOID PASCAL BringItemToTop(PGROUP pGroup, PITEM pItem, BOOL fUpdate)
  {
      PITEM pItemT;
      HRGN hrgn;
  
      if (pItem == pGroup->pItems) {
          return;
      }
  
      if (hrgn = IconExcludedRgn(pGroup, pItem)) {
          InvalidateRgn(pGroup->hwnd, hrgn, TRUE);
          DeleteObject(hrgn);
      }
  
      /*
       * At this point we know there is at least two items, and we're not the
       * first one...
       */
  
      for (pItemT = pGroup->pItems; pItemT->pNext != pItem; pItemT = pItemT->pNext)
          ;
  
      pItemT->pNext = pItem->pNext;
      pItem->pNext = pGroup->pItems;
      pGroup->pItems = pItem;
  
      /*
       * Invalidate the whole titles in order to change the color.
       */
      if (fUpdate) {
          InvalidateRect(pGroup->hwnd, &pItem->rcTitle, TRUE);
          InvalidateRect(pGroup->hwnd, &pItem->pNext->rcTitle, TRUE);
      }
  }
  
  VOID PASCAL ClickOn(HWND hwnd, POINTS pts)
  {
      PGROUP pGroup;
      PITEM pItem;
      POINT pt;
  
      pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
  
      pItem = ItemHitTest(pGroup, pts);
      if (!pItem) {
          return;
      }
  
      BringItemToTop(pGroup, pItem, TRUE);
      ViewActiveItem(pGroup);
  
      pt.x = (int)pts.x;
      pt.y = (int)pts.y;
  
      *(LPPOINT)&rcDrag.left = pt;
      *(LPPOINT)&rcDrag.right = pt;
      hwndDrag = hwnd;
  
      InflateRect(&rcDrag, GetSystemMetrics(SM_CXDOUBLECLK) / 2,
              GetSystemMetrics(SM_CYDOUBLECLK) / 2);
  }
  
  
  VOID PASCAL DragItem(HWND hwnd)
  {
      PGROUP pGroup;
      PITEM pItem;
      HICON hIcon;
  
      pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
      pItem = pGroup->pItems;
  
      if (!pItem || hwndDrag != hwnd)
          goto ProcExit;
  
      /* If the control key isn't down, do a Move operation. */
      bMove = !(GetKeyState(VK_CONTROL) & 0x8000);
  
      /* Don't allow "moves" from RO groups. */
      if (pGroup->fRO && bMove == TRUE)
          goto ProcExit;
  
      /*
       * Redraw the window minus the item we're moving.
       * REVIEW - if you just painted the background colour into the
       * pItem->rcIcon area then you could remove the bMove code from
       * PaintGroup().
       */
      if (bMove) {
          InvalidateIcon(pGroup,pItem);
          UpdateWindow(hwnd);
      }
  
      hIcon = GetItemIcon(hwnd,pItem);
  
      // BUG BUG  MAKELONG(pGroup,pItem) doesn't make sense since all
      // pointers all LOMG in WIN32. Will need to change the parameters!
      // johannec 08-19-91
      if (DragObject(hwndMDIClient, hwnd, (UINT)OBJ_ITEM,
                     MAKELONG(pGroup,pItem), hIcon) == DRAG_COPY) {
          if (bMove)
              DeleteItem(pGroup,pItem);
      }
      else {
          /* Drag was SWP or drag failed... just show the item. */
          if (bMove) {
              bMove = FALSE;
              InvalidateIcon(pGroup,pItem);
          }
      }
      DestroyIcon(hIcon);
  ProcExit:
      bMove = FALSE;
  }
  
  
  void PASCAL GetRealClientRect(
      HWND   hwnd,
      DWORD  dwStyle,
      LPRECT lprcClient)
  {
      DWORD Style;
  
      Style = GetWindowLong(hwnd, GWL_STYLE);
  
          /*BUG BUG will GWL_STYLE work???*/
  
      SetWindowLong(hwnd,GWL_STYLE,dwStyle);
      GetWindowRect(hwnd,lprcClient);
      ScreenToClient(hwnd,(LPPOINT)&lprcClient->left);
      ScreenToClient(hwnd,(LPPOINT)&lprcClient->right);
      SendMessage(hwnd,WM_NCCALCSIZE,0,(LPARAM)lprcClient);
  }
  
  
  VOID APIENTRY CalcGroupScrolls(HWND hwnd)
  {
      register PGROUP pGroup;
      register PITEM pItem;
      RECT rcClient;
      RECT rcRange;
      RECT rcT;
      DWORD dwStyle, dwStyleNew, dwStyleT;
      int iMinPos, iMaxPos;
  
      if (bNoScrollCalc || IsIconic(hwnd))
          return;
  
      // Stop re-entrance of this routine.
      bNoScrollCalc = TRUE;
  
      pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
  
      if (!pGroup->pItems) {
          // No items...
          SetRectEmpty(&rcRange);
      	goto ChangeStyle;
      }
  
      hwndScrolling = hwnd;
  
      // If the user has selected auto arranging then make
      // the next item in the z-order visable.
      if (bAutoArrange)
          ViewActiveItem(pGroup);
  
      SetRectEmpty(&rcRange);
  
      for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext)
        {
          UnionRect(&rcRange,&rcRange,&pItem->rcIcon);
      	rcT.top = pItem->rcTitle.top;		// don't include the
  	    rcT.bottom = pItem->rcTitle.bottom;	// title overhang part
      	rcT.left = pItem->rcIcon.left;
  	    rcT.right = pItem->rcIcon.right;
          UnionRect(&rcRange,&rcRange,&rcT);
        }
  
      if (rcRange.left != rcRange.right)
        {
          // Add on a bit for the left border here.
          rcRange.left -= ((cxArrange-cxIconSpace)/2)+cxOffset;
          // Don't add on a right border so we can cram as many icons in as poss.
          // rcRange.right += ((cxArrange-cxIconSpace)/2);
  
          // ADJUST THE RECT SO THAT WE DON'T GET SCROLL BARS IF ONLY THE BORDERS
          // OF TEXT ARE NOT VISIBLE ~~~
        }
  
  ChangeStyle:
  
      dwStyleNew = dwStyle = GetWindowLong(hwnd,GWL_STYLE);
  
      dwStyleNew &= ~(WS_HSCROLL | WS_VSCROLL);
  
      for (;;)
        {
          dwStyleT = dwStyleNew;
          GetRealClientRect(hwnd, dwStyleNew, &rcClient);
  
          if (rcRange.left < rcClient.left || rcRange.right > rcClient.right)
              dwStyleNew |= WS_HSCROLL;
          if (rcRange.top < rcClient.top || rcRange.bottom > rcClient.bottom)
              dwStyleNew |= WS_VSCROLL;
  
          if (dwStyleNew == dwStyleT)
              break;
        }
  
      if (dwStyleNew == dwStyle && !(dwStyle & (WS_VSCROLL|WS_HSCROLL)))
        {
          /* none there and don't need to add 'em!
           */
          goto ProcExit;
        }
  
      UnionRect(&rcRange,&rcClient,&rcRange);
  
      /* union garantees that left==right or top==bottom in case of no
       * scrollbar.
       */
      rcRange.right -= rcClient.right-rcClient.left;
      rcRange.bottom -= rcClient.bottom-rcClient.top;
  
      /* if the style changed, don't redraw in sb code, just redraw the
       * frame at the end cause the whole ncarea has to be repainted
       * if it hasn't changed, just move the thumb
       */
  
      if (dwStyleNew==dwStyle)
        {
          if (dwStyleNew & WS_HSCROLL)
            {
              if (GetScrollPos(hwnd,SB_HORZ)!=0)
                  goto SetScrollInfo;
              GetScrollRange(hwnd,SB_HORZ,&iMinPos,&iMaxPos);
              if ((iMinPos != rcRange.left) || (iMaxPos != rcRange.right))
                  goto SetScrollInfo;
            }
          if (dwStyleNew & WS_VSCROLL)
            {
              if (GetScrollPos(hwnd,SB_VERT)!=0)
                  goto SetScrollInfo;
              GetScrollRange(hwnd,SB_VERT,&iMinPos,&iMaxPos);
              if ((iMinPos != rcRange.top) || (iMaxPos != rcRange.bottom))
                  goto SetScrollInfo;
            }
          goto ProcExit;
        }
  
  SetScrollInfo:
      SetScrollPos(hwnd,SB_HORZ,0,FALSE);
      SetScrollPos(hwnd,SB_VERT,0,FALSE);
      SetScrollRange(hwnd,SB_HORZ,rcRange.left,rcRange.right,FALSE);
      SetScrollRange(hwnd,SB_VERT,rcRange.top,rcRange.bottom,FALSE);
  
      SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE
                                           | SWP_NOMOVE | SWP_NOACTIVATE
                                           | SWP_DRAWFRAME);
  ProcExit:
      // Allow other scroll calculations.
      bNoScrollCalc = FALSE;
  }
  
  
  VOID PASCAL ScrollGroup(PGROUP pGroup, int xMove, int yMove, BOOL fCalc)
  {
      register PITEM pItem;
  
      for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext)
        {
          OffsetRect(&pItem->rcIcon,xMove,yMove);
          OffsetRect(&pItem->rcTitle,xMove,yMove);
        }
      ScrollWindow(pGroup->hwnd,xMove,yMove,NULL,NULL);
  
      UpdateWindow(pGroup->hwnd);
  
      if (fCalc)
          CalcGroupScrolls(pGroup->hwnd);
  }
  
  VOID APIENTRY ViewActiveItem(PGROUP pGroup)
  {
      RECT rcClient, rc;
      int xMove = 0, yMove = 0;
  
      GetClientRect(pGroup->hwnd,&rcClient);
  
      UnionRect(&rc, &pGroup->pItems->rcIcon, &pGroup->pItems->rcTitle);
      // Clip width to that of icon i.e. ignore width of text.
      rc.left = pGroup->pItems->rcIcon.left;
      rc.right = pGroup->pItems->rcIcon.right;
  
  
      if (rc.left < rcClient.left)
          xMove = rcClient.left - rc.left;
      else if (rc.right > rcClient.right)
          xMove = rcClient.right - rc.right;
  
      if (rc.top < rcClient.top)
          yMove = rcClient.top - rc.top;
      else if (rc.bottom > rcClient.bottom)
          yMove = rcClient.bottom - rc.bottom;
  
      if (xMove || yMove)
          ScrollGroup(pGroup, xMove, yMove,TRUE);
  }
  
  
  BOOL FAR PASCAL CheckHotKey(WPARAM wParam, LPARAM lParam)
  {
      HWND hwndT;
      PGROUP pGroup;
      PITEM pItem;
  
      switch (wParam)
      {
      case VK_SHIFT:
      case VK_CONTROL:
      case VK_MENU:
      case VK_RETURN:
          return FALSE;
      }
  
      if (GetKeyState(VK_SHIFT) < 0) {
      	// DBG((" + SHIFT"));
  	    wParam |= HK_SHIFT;
      }
      if (GetKeyState(VK_CONTROL) < 0) {
      	// DBG((" + CONTROL"));
  	    wParam |= HK_CONTROL;
      }
      if (GetKeyState(VK_MENU) < 0) {
      	// DBG((" + ALT"));
  	    wParam |= HK_ALT;
      }
      if (lParam & F_EXT) {
      	// DBG((" EXTENDED"));
  	    wParam |= HK_EXT;
      }
  
      // DBG(("... Full code %4.4X...\r\n",wParam));
  
      for (hwndT = GetWindow(hwndMDIClient,GW_CHILD);
            hwndT;
            hwndT = GetWindow(hwndT,GW_HWNDNEXT)) {
          if (GetWindow(hwndT,GW_OWNER))
              continue;
  
          pGroup = (PGROUP)GetWindowLongPtr(hwndT,GWLP_PGROUP);
  
          for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
      	    // DBG(("Checking (%4.4X,%4.4X)...\r\n",pGroup,pItem));
              if (GroupFlag(pGroup,pItem,(WORD)ID_HOTKEY) == (WORD)wParam) {
  	        	// DBG(("F*O*U*N*D\r\n"));
                  ExecItem(pGroup,pItem,FALSE,FALSE);
                  return TRUE;
              }
          }
      }
  
      return FALSE;
  }
  
  
  VOID APIENTRY KeyWindow(HWND hwnd, WORD wDirection)
  {
      int     wT;
      int     wNext;
      RECT    rc;
      RECT    rcT;
      POINT   ptA;
      POINT   ptT;
      PGROUP  pGroup;
      PITEM   pItem, pItemNext;
  
      pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
  
      if (!pGroup->pItems)
          return;
  
      wNext = 0x7FFF;
      pItemNext = NULL;
      CopyRect(&rc,&pGroup->pItems->rcIcon);
  
      for (pItem = pGroup->pItems->pNext; pItem; pItem = pItem->pNext) {
          CopyRect(&rcT,&pItem->rcIcon);
          ptT.x = rcT.left - rc.left;
          ptT.y = rcT.top - rc.top;
          ptA.x = (ptT.x < 0) ? -ptT.x : ptT.x;
          ptA.y = (ptT.y < 0) ? -ptT.y : ptT.y;
  
          switch (wDirection) {
              case VK_LEFT:
                  if ((ptT.x >= 0) || (ptA.x < ptA.y))
                      continue;
                  break;
  
              case VK_RIGHT:
                  if ((ptT.x <= 0) || (ptA.x < ptA.y))
                      continue;
                  break;
  
              case VK_DOWN:
                  if ((ptT.y <= 0) || (ptA.y < ptA.x))
                      continue;
                  break;
  
              case VK_UP:
                  if ((ptT.y >= 0) || (ptA.y < ptA.x))
                      continue;
                  break;
  
              default:
                  /* illegal key
                   */
                  return;
          }
  
          wT = ptA.y + ptA.x;
  
          if (wT <= wNext) {
              wNext = wT;
              pItemNext = pItem;
          }
      }
  
      if (pItemNext) {
          BringItemToTop(pGroup,pItemNext, TRUE);
          ViewActiveItem(pGroup);
      }
  }
  
  
  VOID APIENTRY CharWindow(register HWND hwnd, register WORD wChar)
  {
      LPGROUPDEF lpgd;
      LPITEMDEF lpid;
      PGROUP pGroup;
      PITEM pItem, pItemLast;
  
      pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
  
      if (!pGroup->pItems)
          return;
  
      lpgd = LockGroup(hwnd);
      if (!lpgd)
          return;
  
      /* Search for item, skip the currently selected one.*/
      for ( pItem = pGroup->pItems->pNext; pItem; pItem=pItem->pNext)
        {
          lpid = ITEM(lpgd,pItem->iItem);
          if (CharUpper((LPTSTR)(DWORD)wChar)
            == CharUpper((LPTSTR)(DWORD)(BYTE)*PTR(lpgd, lpid->pName)))
            {
              pItemLast = MakeFirstItemLast(pGroup);
              BringItemToTop(pGroup,pItem, FALSE);
              /* Handle updates.*/
              InvalidateRect(pGroup->hwnd,&pItem->rcTitle,TRUE);
              InvalidateRect(pGroup->hwnd,&pItemLast->rcTitle,TRUE);
              ViewActiveItem(pGroup);
              break;
            }
        }
      UnlockGroup(hwnd);
  }
  
  
  VOID APIENTRY ScrollMessage(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  {
      int           wMin;
      int           wMax;
      int           wPos;
      int           wInc;
      int           wPage;
      int           wNewPos;
      RECT          rcClient;
      int           yMove;
      int           xMove;
      BOOL          fTemp;
  
      GetClientRect(hwnd, &rcClient);
  
      if (uiMsg == WM_HSCROLL) {
          GetScrollRange(hwnd, SB_HORZ, &wMin, &wMax);
          wPos = GetScrollPos(hwnd, SB_HORZ);
          wInc = cxIconSpace + cxArrange / 2;
          wPage = rcClient.right-rcClient.left;
      }
      else {
          GetScrollRange(hwnd, SB_VERT, &wMin, &wMax);
          wPos = GetScrollPos(hwnd, SB_VERT);
          wInc = cyArrange;
          wPage = rcClient.bottom-rcClient.top;
      }
  
      switch (GET_WM_VSCROLL_CODE(wParam, lParam)) {
          case SB_BOTTOM:
              wNewPos = wMax;
              break;
  
          case SB_TOP:
              wNewPos = wMin;
              break;
  
          case SB_LINEDOWN:
              wNewPos = wPos + wInc;
              break;
  
          case SB_LINEUP:
              wNewPos = wPos - wInc;
              break;
  
          case SB_PAGEDOWN:
              wNewPos = wPos + wPage;
              break;
  
          case SB_PAGEUP:
              wNewPos = wPos - wPage;
              break;
  
          case SB_THUMBTRACK:
          case SB_THUMBPOSITION:
              wNewPos = (INT)(SHORT)GET_WM_VSCROLL_POS(wParam, lParam);
              break;
  
          case SB_ENDSCROLL:
              // We might suddenly not need the scroll bars anymore so
              // check now.
              // Stop CGS from moving the view.
              fTemp = bAutoArrange;
              bAutoArrange = FALSE;
      	    CalcGroupScrolls(hwnd);
              bAutoArrange = fTemp;
  
              /*** FALL THRU ***/
  
          default:
              return;
      }
  
      if (wNewPos < wMin)
          wNewPos = wMin;
      else if (wNewPos > wMax)
          wNewPos = wMax;
  
      if (uiMsg == WM_VSCROLL) {
          SetScrollPos(hwnd, SB_VERT, wNewPos, TRUE);
          yMove = wPos - wNewPos;
          xMove = 0;
      }
      else {
          SetScrollPos(hwnd, SB_HORZ, wNewPos, TRUE);
          yMove = 0;
          xMove = wPos - wNewPos;
      }
  
      ScrollGroup((PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP),xMove,yMove,FALSE);
  }
  
  
  VOID PASCAL OfficialRect(
      LPRECT lprc,
      int x,
      int y,
      int xOffset,
      int yOffset)
  {
      // Work out were the icon should go in the icon grid, taking
      // note of where the scroll bars are.
  
      lprc->right = (lprc->left = x-xOffset + (cxIconSpace - cxArrange) / 2) +
              cxArrange - 1;
      lprc->bottom = (lprc->top = y-yOffset) + cyArrange - 1;
  }
  
  
  BOOL PASCAL IconOverlaps(
      PGROUP pGroup,
      LPRECT lprc,
      int xOffset,
      int yOffset)
  {
      PITEM pItem;
      RECT rcT;
  
      for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
          // Ignore icons at -1. This is where icon's get put when
          // we don't know where to put them and they will get moved
          // later.
          if (pItem->rcIcon.left == -1) {
              continue;
          }
  
          OfficialRect(&rcT, pItem->rcIcon.left, pItem->rcIcon.top, xOffset, yOffset);
          if (IntersectRect(&rcT, &rcT, lprc)) {
              return TRUE;
          }
      }
      return FALSE;
  }
  
  
  /*
   * NB This is called for every icon at init time so put anything to do with
   * finding icon positions inside the `if' because that's skipped on init.
   * If you don't it'll get tres slow.
   */
  
  VOID PASCAL ComputeIconPosition(
      PGROUP pGroup,
      POINT pt,
      LPRECT lprcIcon,
      LPRECT lprcTitle,
      LPTSTR lpText)
  {
      HDC hdc;
      int cch;
      RECT rcClient, rcT;
      HFONT hFontT;
      int xsp, ysp;       // Current position of scrollbar.
      int vMax, vMin;     // Range.
      int hMax, hMin;     // Range.
      int xOffset, yOffset;
      DWORD dwStyle;
  
      if (pt.x == -1) {
          /*
           * Icon is in "find me a default position" mode...
           * so search the icon space for it...
           */
          // Get the current window style.
          dwStyle = GetWindowLong(pGroup->hwnd,GWL_STYLE);
  
          if (dwStyle & WS_MINIMIZE) {
              // DBG(("PM.CIP: Window Minimised\n\r"));
              // We want to use the restored state of the window.
              GetInternalWindowPos(pGroup->hwnd, &rcClient, NULL);
              // Convert from screen coords to client coords.
              OffsetRect(&rcClient, -rcClient.left, -rcClient.top);
          }
          else {
              // DBG(("PM.CIP: Window normal or maxed.\n\r"));
              // Take into account scroll bars.
              GetClientRect(pGroup->hwnd, &rcClient);
          }
  
          if (dwStyle & WS_HSCROLL) {
               xsp = GetScrollPos(pGroup->hwnd, SB_HORZ);
               GetScrollRange(pGroup->hwnd, SB_HORZ, &hMin, &hMax);
               xOffset = xsp-hMin;     // Offset icon grid to match scroll bar pos.
          }
          else {
               xOffset = 0;
          }
  
          if (dwStyle & WS_VSCROLL) {
               ysp = GetScrollPos(pGroup->hwnd, SB_VERT);
               GetScrollRange(pGroup->hwnd, SB_VERT, &vMin, &vMax);
               yOffset = ysp-vMin;     // Offset icon grid.
          }
          else {
               yOffset = 0;
          }
  
          pt.x = (cxArrange - cxIconSpace) / 2 + cxOffset - xOffset;
          pt.y = 0 - yOffset;
          /* Set this icon's left to -1 so that it'll be excluded
           * by the IconOverlaps check.
           */
          lprcIcon->left = -1;
  
          for (;;) {
              OfficialRect(&rcT, pt.x, pt.y, xOffset, yOffset);
  
              if (!IconOverlaps(pGroup, &rcT, xOffset, yOffset)) {
                  break;
              }
  
              if (rcT.right + cxArrange > rcClient.right) {
                  pt.x = (cxArrange-cxIconSpace)/2 + cxOffset - xOffset;
                  pt.y += cyArrange;
              }
              else {
                  pt.x += cxArrange;
              }
          }
      }
  
      SetRect(lprcIcon, pt.x, pt.y, pt.x+cxIconSpace, pt.y+cyIconSpace);
  
      if (IsRectEmpty(lprcTitle)) {
          cch = lstrlen(lpText);
  
          hdc = GetDC(pGroup->hwnd);
          hFontT = SelectObject(hdc, hFontTitle);
  
          /*
           * Compute the icon rect using DrawText.
           */
          lprcTitle->right = cxArrange - (2 * cxOffset);
          DrawText(hdc, lpText, -1, lprcTitle, bIconTitleWrap ?
              (WORD)(DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX) :
              (WORD)(DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX | DT_SINGLELINE));
  
          if (hFontT) {
              SelectObject(hdc, hFontT);
          }
          ReleaseDC(pGroup->hwnd, hdc);
          lprcTitle->right += cxOffset * 2;
          lprcTitle->bottom += dyBorder * 2;
  
      }
      else {
          SetRect(lprcTitle, 0, 0, lprcTitle->right - lprcTitle->left,
                  lprcTitle->bottom - lprcTitle->top);
      }
  
  
      OffsetRect(lprcTitle, pt.x+(cxIconSpace/2)-((lprcTitle->right-lprcTitle->left)/2),
                    pt.y + cyIconSpace - dyBorder);
  
  // REVIEW Very expensive to do this here.
  //    if ((bAutoArrange) && (!bAutoArranging))
  //	      ArrangeItems(pGroup->hwnd);
  }
  
  VOID APIENTRY MoveItem(PGROUP pGroup, PITEM pItem, POINT pt)
  {
      LPITEMDEF lpid;
      LPGROUPDEF lpgd;
  
      lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
  
      /*
       * If the position is the same, ignore
       */
      if ((pt.x == pItem->rcIcon.left) && (pt.y == pItem->rcIcon.top)) {
          GlobalUnlock(pGroup->hGroup);
          return;
      }
  
      /*
       * Repaint the original position
       */
      InvalidateIcon(pGroup, pItem);
  
      lpid = LockItem(pGroup,pItem);
      if (!lpid) {
          GlobalUnlock(pGroup->hGroup);
          return;
      }
  
      ComputeIconPosition(pGroup, pt, &pItem->rcIcon, &pItem->rcTitle,
              (LPTSTR) PTR(lpgd, lpid->pName));
  
  
      UnlockGroup(pGroup->hwnd);
      GlobalUnlock(pGroup->hGroup);
  
      /*
       * Repaint the new position
       */
      InvalidateIcon(pGroup,pItem);
  
  //  CalcGroupScrolls(pGroup->hwnd);
  }
  
  /*--------------------------------------------------------------------------*/
  /*                                                                          */
  /*  DropObject() -                                                          */
  /*                                                                          */
  /*--------------------------------------------------------------------------*/
  
  LONG NEAR PASCAL DropObject(HWND hWnd, LPDROPSTRUCT lpds)
  {
    BOOL          fNC;
    BOOL          fOk;
    POINT         pt;
    LPPOINT       lppt;
    PGROUP        pGroup;
    PITEM         pItem;
    RECT          rcClient;
  
    pGroup = pCurrentGroup;
    pItem = pGroup->pItems;
  
    pt = lpds->ptDrop;
  
    // A drop anywhere in the window is valid.
    GetWindowRect(hWnd, &rcClient);
    // Convert to client coords.
    ScreenToClient(hWnd,(LPPOINT)&(rcClient.left));
    ScreenToClient(hWnd,(LPPOINT)&(rcClient.right));
  
    if (pt.x >= rcClient.left && pt.y >= rcClient.top && pt.x <= rcClient.right
                                                  && pt.y <= rcClient.bottom) {
        /* Dropped in given point of client area. */
        fNC = FALSE;
        pt.x -= (GetSystemMetrics(SM_CXICON) / 2) + 2;
        pt.y -= (GetSystemMetrics(SM_CYICON) / 2) + 2;
        lppt = &pt;
    }
    else {
        /* Dropped in nonclient area. */
        fNC = TRUE;
        lppt = NULL;
    }
  
    /* Are we iconic ? */
    if (IsIconic(hWnd)) {
        // Yep, we'll need to use default positioning.
        fNC = TRUE;
        lppt = NULL;
    }
  
  #if 0
    // this if statement code if obsolete, it is never called. - johannec 8/11/93
    if (lpds->wFmt == DOF_EXECUTABLE || lpds->wFmt == DOF_DOCUMENT) {
  
        BuildDescription(szNameField, szPathName);
  
        return((LONG)(CreateNewItem(hWnd,
                  szNameField, szPathName, szPathName, TEXT(""),
                  0, FALSE, 0, 0, NULL, lppt, CI_SET_DOS_FULLSCRN) != NULL));
    }
  #endif
  
    if ((hWnd == pGroup->hwnd) && (bMove)) {
        /* Don't drop on our own non-client area. */
        if (fNC)
            return 0L;
  
        /* We are just moving the item within its own group.
         * Hide it first so the icon title is treated correctly.
         */
        MoveItem(pGroup,pItem, pt);
        if ((bAutoArrange) && (!bAutoArranging))
            ArrangeItems(pGroup->hwnd);
        else if (!bAutoArranging)
            CalcGroupScrolls(pGroup->hwnd);
  
        return(DRAG_SWP);
    }
    else {
        /* Copy the item to the new group...  Set the hourglass
         * cursor (it will get unset after the message returns),
         * select the new group, and add the item at the specified
         * point.
         */
        fOk = DuplicateItem(pGroup,pItem,
                    (PGROUP)GetWindowLongPtr(hWnd,GWLP_PGROUP),lppt) != NULL;
  
        /*
         * Re-Arrange items within the destination group.
         * NB The source will been taken care of by the DeleteItem routine
         * called from DragItem.
         */
        if ((bAutoArrange) && (!bAutoArranging)) {
            /* Destination */
            ArrangeItems(hWnd);
        }
        else if (!bAutoArranging) {
            /* Destination */
            CalcGroupScrolls(hWnd);
        }
  
        /* View the current item. */
        BringItemToTop(pGroup,pItem, TRUE);
        ViewActiveItem(pGroup);
  
        /* If the dest isn't minimised then move the focus to it. */
        if (!IsIconic(hWnd))
             SetFocus(hWnd);
        return (fOk ? DRAG_COPY : 0L);
    }
  }
  
  LONG APIENTRY DropFiles(HWND hwnd, HANDLE hDrop)
  {
      POINT pt;
      LPPOINT lppt;
      UINT i;
      HCURSOR hCursor;
      DWORD dwRet;
      DWORD dwFlags = CI_ACTIVATE | CI_SET_DOS_FULLSCRN;
  
      hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
      ShowCursor(TRUE);
  
      if (!DragQueryPoint(hDrop, &pt) ||
                     DragQueryFile(hDrop, (UINT)-1, NULL, 0) != 1) {
          lppt = NULL;
      }
      else {
          pt.x -= (GetSystemMetrics(SM_CXICON) / 2) + 2;
          pt.y -= (GetSystemMetrics(SM_CYICON) / 2) + 2;
          lppt = &pt;
      }
  
      for (i=0; DragQueryFile(hDrop, i, szPathField, MAXITEMPATHLEN); i++) {
          //
          // if filename or directory have spaces, put the path
          // between quotes.
          //
          CheckEscapes(szPathField, MAXITEMPATHLEN+1);
  
          /* Verify the file's existance... */
          dwRet = ValidatePath(hwndProgman, szPathField, NULL, szIconPath);
          if (dwRet == PATH_INVALID) {
              continue;
  	    }
  	else if (dwRet == PATH_INVALID_OK) {
  	    dwFlags |= CI_NO_ASSOCIATION;
  	    }
  
          BuildDescription(szNameField,szPathField);
  
          GetDirectoryFromPath(szPathField, szDirField);
          if (!InQuotes(szDirField)) {
              CheckEscapes(szDirField, MAXITEMPATHLEN+1);
          }
  
          HandleDosApps(szIconPath);
  
          if (!CreateNewItem(hwnd,
                        szNameField,                /* name*/
                        szPathField,                /* command*/
                        szIconPath ,                /* icon path*/
                        szDirField,                 /* no default dir*/
                        0,0,                        /* no hotkey, no min on run*/
                        0,0,0,                      /* default icon*/
                        lppt,                       /* at this point*/
                        dwFlags))
                  break;
      }
  
      DragFinish(hDrop);
  
      ShowCursor(FALSE);
      SetCursor(hCursor);
  
      if ((bAutoArrange) && (!bAutoArranging))
          ArrangeItems(hwnd);
      else if (!bAutoArranging)
          CalcGroupScrolls(hwnd);
  
      return 1L;
  }
  
  LRESULT APIENTRY GroupWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  {
      POINT pt;
  
      switch (uiMsg) {
  
      case WM_CREATE:
      {
          LPMDICREATESTRUCT lpmdics;
  
          lpmdics = (LPMDICREATESTRUCT)(((LPCREATESTRUCT)lParam)->lpCreateParams);
          SetWindowLongPtr(hwnd, GWLP_PGROUP, lpmdics->lParam);
          DragAcceptFiles(hwnd,TRUE);
          SetFocus(hwnd);
          break;
      }
  
      case WM_ERASEBKGND:
          if (IsIconic(hwnd)) {
              //
              // Erase background with the APPWORKSPACE color
              //
              RECT rc;
  
  	    if (!hbrWorkspace)
  		hbrWorkspace = CreateSolidBrush(GetSysColor(COLOR_APPWORKSPACE));
              GetUpdateRect(hwnd, &rc, FALSE);
              if (IsRectEmpty(&rc)) {
                  GetClientRect(hwnd, &rc);
              }
              FillRect((HDC)wParam, &rc, hbrWorkspace);
          }
          else {
              goto DefProc;
          }
          break;
  
      case WM_PAINT:
          if (IsIconic(hwnd)) {
              DrawGroupIcon(hwnd);
          }
          else {
              PaintGroup(hwnd);
          }
          break;
  
      case WM_QUERYDRAGICON:
      {
          PGROUP pGroup;
          HICON hIcon = NULL;
  
          pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
  
          if (pGroup->fCommon) {
              hIcon = LoadIcon(hAppInstance, (LPTSTR) MAKEINTRESOURCE(COMMGROUPICON));
          }
          else {
              hIcon = LoadIcon(hAppInstance, (LPTSTR) MAKEINTRESOURCE(PERSGROUPICON));
          }
  
          if (hIcon) {
              return((LRESULT)hIcon);
          }
          else {
              goto DefProc;
          }
  
          break;
      }
  
      case WM_LBUTTONDOWN:
          ClickOn(hwnd, MAKEPOINTS(lParam));
          break;
  
      case WM_MOUSEMOVE:
          if (wParam & MK_LBUTTON) {
              pt.x = (int)(MAKEPOINTS(lParam).x);
              pt.y = (int)(MAKEPOINTS(lParam).y);
              if (!IsRectEmpty(&rcDrag) && !PtInRect(&rcDrag, pt)
                                      && !fNoFileMenu && (dwEditLevel < 2)) {
                  SetRect(&rcDrag,0,0,0,0);
                  DragItem(hwnd);
              }
          }
          else {
              SetRect(&rcDrag,0,0,0,0);
          }
          break;
  
      case WM_LBUTTONUP:
          SetRect(&rcDrag,0,0,0,0);
          break;
  
      case WM_NCLBUTTONDBLCLK:
          if (IsIconic(hwnd) && (GetKeyState(VK_MENU) < 0)) {
              PostMessage(hwndProgman, WM_COMMAND, IDM_PROPS, 0L);
          } else {
              goto DefProc;
          }
          break;
  
      case WM_LBUTTONDBLCLK:
  
          if (ItemHitTest((PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP),
                          MAKEPOINTS(lParam))) {
              if (GetKeyState(VK_MENU) < 0) {
                  if (!fNoFileMenu)
                      PostMessage(hwndProgman,WM_COMMAND,IDM_PROPS,0L);
              } else {
                  PostMessage(hwndProgman,WM_COMMAND,IDM_OPEN,0L);
              }
  
          } else {
              /*
               * Check for Alt-dblclk on nothing to get new item.
               */
              if (GetKeyState(VK_MENU) < 0 && !fNoFileMenu &&
                               (dwEditLevel <= 1) && !(pCurrentGroup->fRO) ) {
                  MyDialogBox(ITEMDLG, hwndProgman, NewItemDlgProc);
              }
          }
          break;
  
      case WM_VSCROLL:
      case WM_HSCROLL:
          ScrollMessage(hwnd,uiMsg,wParam,lParam);
          break;
  
      case WM_CLOSE:
          SendMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0L);
          break;
  
      case WM_SYSCOMMAND:
      {
          PGROUP pGroup;
          LPGROUPDEF lpgd;
          TCHAR szCommonGroupSuffix[MAXKEYLEN];
          TCHAR szCommonGroupTitle[2*MAXKEYLEN];
  
          if (wParam == SC_MINIMIZE) {
              //
              // if the group is common remove the common suffix from the group
              // window title
              //
              pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
              if (pGroup->fCommon) {
                  lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
                  SetWindowText(hwnd, (LPTSTR) PTR(lpgd, lpgd->pName));
                  GlobalUnlock(pGroup->hGroup);
              }
          }
  
          if (wParam == SC_RESTORE) {
              if (!LockGroup(hwnd)) {
                  if (wLockError == LOCK_LOWMEM) {
                      MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_LOWMEM, NULL, MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
                      break;
                  }
                  else {
                      /*
                       * Lock failed for some other reason - hopefully just
                       * a group change.  Stop the icon group from being
                       * restored.
                       */
                      break;
                  }
              }
              else {
          		    UnlockGroup(hwnd);
  		      }
          }
      	  if ((wParam == SC_MAXIMIZE) || (wParam == SC_RESTORE)) {
              //
              // if the group is common add the common suffix to the group
              // window title
              //
              pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
              if (pGroup->fCommon) {
                  lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
  
                  if (!lpgd)
                     goto DefProc;
  
                  lstrcpy(szCommonGroupTitle, (LPTSTR) PTR(lpgd, lpgd->pName));
                  GlobalUnlock(pGroup->hGroup);
                  if (LoadString(hAppInstance, IDS_COMMONGRPSUFFIX, szCommonGroupSuffix,
                                 CharSizeOf(szCommonGroupSuffix))) {
                      lstrcat(szCommonGroupTitle, szCommonGroupSuffix);
                  }
                  SetWindowText(pGroup->hwnd, szCommonGroupTitle);
             }
  	        InvalidateRect(hwnd, NULL, 0);
          }
          if (wParam == SC_MAXIMIZE)
  	         SetWindowLong(hwnd, GWL_STYLE,
  	              (GetWindowLong(hwnd,GWL_STYLE) & ~(WS_HSCROLL | WS_VSCROLL)));
      	  goto DefProc;
  			
      }
      case WM_SYSKEYDOWN:
          if (!CheckHotKey(wParam,lParam))
              goto DefProc;
          break;
  
      case WM_KEYDOWN:
          if (!CheckHotKey(wParam,lParam))
              KeyWindow(hwnd,(WORD)wParam);
          break;
  
      //IME Support
      //by yutakas 1992.10.22
      // When user input DBCS, go and activate icon which has that
      // DBCS charcter in the first of description.
      case WM_IME_REPORT:
          switch (wParam)
          {
              case IR_STRING:
                  IMEStringWindow(hwnd,(HANDLE)lParam);
                  return TRUE;
              default:
                  goto DefProc;
          }
          break;
  
      case WM_CHAR:
          CharWindow(hwnd, (WORD) wParam);
          break;
  
      case WM_QUERYDROPOBJECT:
      {
          #define lpds ((LPDROPSTRUCT)lParam)
  
          PGROUP pGroup;
  
          pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
  
          if (pGroup->fRO) {
              return FALSE;
          }
  
      	if (lpds->wFmt == OBJ_ITEM) {
      	    return TRUE;
          }
          #undef lpds
      	goto DefProc;
      }
  
      case WM_DROPOBJECT:
          #define lpds ((LPDROPSTRUCT)lParam)
  
          if (lpds->wFmt == OBJ_ITEM)
              return DropObject(hwnd, lpds);
          #undef lpds
          goto DefProc;
  
      case WM_DROPFILES:
          return DropFiles(hwnd,(HANDLE)wParam);
  
      case WM_NCACTIVATE:
      {
          PGROUP pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
  
          if (pGroup->pItems != NULL) {
              InvalidateRect(hwnd,&pGroup->pItems->rcTitle,TRUE);
          }
          goto DefProc;
      }
  
      case WM_QUERYOPEN:
      {
          PGROUP pGroup;
          LPGROUPDEF lpgd;
          TCHAR szCommonGroupSuffix[MAXKEYLEN];
          TCHAR szCommonGroupTitle[2*MAXKEYLEN];
  
          //
          // if the group is common add the common suffix to the group
          // window title
          //
          pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
          if (pGroup->fCommon) {
              lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
  
              if (!lpgd)
                 goto DefProc;
  
              lstrcpy(szCommonGroupTitle,(LPTSTR) PTR(lpgd, lpgd->pName));
              GlobalUnlock(pGroup->hGroup);
              if (LoadString(hAppInstance, IDS_COMMONGRPSUFFIX, szCommonGroupSuffix,
                             CharSizeOf(szCommonGroupSuffix))) {
                  lstrcat(szCommonGroupTitle, szCommonGroupSuffix);
              }
              SetWindowText(pGroup->hwnd, szCommonGroupTitle);
          }
          goto DefProc;
      }
  
      case WM_SIZE:
          lParam = DefMDIChildProc(hwnd, uiMsg, wParam, lParam);
          if (wParam != SIZEICONIC) {
              if ((bAutoArrange) && (!bAutoArranging)) {
                  ArrangeItems(hwnd);
              } else if (!bArranging) {
                  CalcGroupScrolls(hwnd);
              }
          }
          else {
              PGROUP pGroup;
              LPGROUPDEF lpgd;
  
              //
              // reset window text of common groups
              //
              pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
              if (pGroup->fCommon) {
                  lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
                  SetWindowText(pGroup->hwnd, (LPTSTR) PTR(lpgd, lpgd->pName));
                  GlobalUnlock(pGroup->hGroup);
              }
          }
          return lParam;
  
      case WM_MDIACTIVATE:
          if (!pCurrentGroup) {
              goto DefProc;
          }
  
          /*
           * If we are de-activating this window...
           */
          if (lParam == 0) {
              /*
               * We're the last window... punt.
               */
              pCurrentGroup = NULL;
  
          } else if (hwnd == (HWND)wParam) {
              /*
               * We're being deactivated.  Update pCurrentGroup
               * to the node being activated.
               */
              pCurrentGroup = (PGROUP)GetWindowLongPtr((HWND)lParam, GWLP_PGROUP);
  
          } else {
              SetFocus(hwnd);
          }
  
          goto DefProc;
  
      case WM_MENUSELECT:
          //
          // to handle F1 on group window system menu
          //
  
          if (lParam) {        /*make sure menu handle isn't null*/
              wMenuID =  GET_WM_COMMAND_ID(wParam, lParam);    /*get cmd from loword of wParam*/
              hSaveMenuHandle = (HANDLE)lParam;    /*Save hMenu into one variable*/
              wSaveFlags = HIWORD(wParam);/*Save flags into another*/
              bFrameSysMenu = FALSE;
          }
  
          break;
  
      default:
  DefProc:
          return DefMDIChildProc(hwnd, uiMsg, wParam, lParam);
      }
  
      return 0L;
  }
  
  
  /*--------------------------------------------------------------------------*/
  /*                                                                          */
  /*  ArrangeItems() -                                                        */
  /*                                                                          */
  /* Arranges iconic windows within a group.                                  */
  /*                                                                          */
  /*--------------------------------------------------------------------------*/
  
  VOID FAR PASCAL ArrangeItems(HWND hwnd)
  {
      PGROUP pGroup;
      register PITEM pItem;
      PITEM pItemT;
      int xSlots;
      register int i;
      int j,k;
      RECT rc;
      LPGROUPDEF lpgd;
      PITEM rgpitem[CITEMSMAX];
      POINT pt;
      int t1, t2;
      LONG style;
  
      if (bAutoArranging || IsIconic(hwnd))
          return;
  
      pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
      if (!pGroup)
          return;
  
      /*
       * If the group is RO then don't rearrange the items, just update the
       * scroll bars
       */
      if (!GroupCheck(pGroup)) {
          CalcGroupScrolls(hwnd);
          return;
      }
  
      bAutoArranging = TRUE;
      SetRectEmpty(&rcArrangeRect);
  
      style = GetWindowLong(hwnd,GWL_STYLE);
      GetRealClientRect(hwnd,style&~(WS_VSCROLL|WS_HSCROLL),&rc);
      SetWindowLong(hwnd,GWL_STYLE,style);
  
      xSlots = (rc.right - rc.left)/cxArrange;
      if (xSlots < 1)
          xSlots = 1;
  
      /* sort the items by x location within a row, or by row if the
       * rows are different
       */
      k = 0;
      for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
          /* find nearest row
           */
          t1 = pItem->rcIcon.top + cyArrange/2;
          if (t1 >= 0)
  	        t1 -= t1 % cyArrange;
          else
  	        t1 += t1 % cyArrange;
  
          for (i = 0; i < k; i++) {
              pItemT = rgpitem[i];
  
              t2 = pItemT->rcIcon.top + cyArrange/2;
              if (t2 >= 0)
  	            t2 -= t2 % cyArrange;
              else
  	            t2 += t2 % cyArrange;
  
              if (t2 > t1)
                  break;
              else if (t2 == t1 && pItemT->rcIcon.left > pItem->rcIcon.left)
                  break;
          }
  
          for (j = k; j > i; j--) {
              rgpitem[j] = rgpitem[j-1];
          }
  
          rgpitem[i] = pItem;
  
          k++;
      }
  
      lpgd = LockGroup(hwnd);
      if (!lpgd) {
          bAutoArranging = FALSE;
          return;
      }
  
      bNoScrollCalc = TRUE;
      for (i = 0; i < k; i++) {
          pItem = rgpitem[i];
  
          /* cxOffset necessary to match (buggy???) win 3 USER
           */
          pt.x = (i%xSlots)*cxArrange + (cxArrange-cxIconSpace)/2 + cxOffset;
          pt.y = (i/xSlots)*cyArrange;
  
          MoveItem(pGroup,pItem,pt);
      }
  
      if (!IsRectEmpty(&rcArrangeRect))
          InvalidateRect(pGroup->hwnd,&rcArrangeRect,TRUE);
  
      UnlockGroup(hwnd);
      bNoScrollCalc = FALSE;
      CalcGroupScrolls(hwnd);
  
      bAutoArranging = FALSE;
  }
  
  /****************************************************************************
   *
   *  IMEStringWindow(hwnd,hstr)
   *
   *  Change activate item by the strings come from IME.
   *  When Get WM_IME_REPORT with IR_STRING,this function is called.
   *
   *                         by yutakas 1992.10.22
   *
   ****************************************************************************/
  BOOL FAR PASCAL IMEStringWindow(HWND hwnd, HANDLE hStr)
  {
      LPTSTR lpStr;
      LPGROUPDEF lpgd;
      LPITEMDEF lpid;
      PGROUP pGroup;
      PITEM pItem = NULL;
      PITEM pItemLast,pTItem;
      int nCnt = 0;
      int nTCnt = 0;
      BOOL ret = FALSE;
  
      if (!hStr)
          return ret;
  
      if (!(lpStr = GlobalLock(hStr)))
          return ret;
  
      pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
  
      if (!pGroup->pItems)
          return ret;
  
      lpgd = LockGroup(hwnd);
      if (!lpgd)
          return ret;
  
  #ifdef _DEBUG
  {
  TCHAR szDev[80];
  OutputDebugString((LPTSTR)TEXT("In IME Winsdow\r\n"));
  wsprintf ((LPTSTR)szDev,TEXT("IMEStringWindow: lpStr is %s \r\n"),lpStr);
  OutputDebugString((LPSTR)szDev);
  }
  #endif
  
  
      // Search for item, skip the currently selected one.
      for ( pTItem = pGroup->pItems->pNext; pTItem; pTItem=pTItem->pNext)
      {
          lpid = ITEM(lpgd,pTItem->iItem);
          nTCnt = IMEWindowGetCnt(lpStr,(LPTSTR)PTR(lpgd,lpid->pName));
          if (nCnt < nTCnt)
          {
              nCnt = nTCnt;
              pItem = pTItem;
          }
      }
  
      lpid = ITEM(lpgd,pGroup->pItems->iItem);
      nTCnt = IMEWindowGetCnt(lpStr,(LPTSTR)PTR(lpgd,lpid->pName));
  
      if ((nCnt >= nTCnt) && pItem)
        {
          pItemLast = MakeFirstItemLast(pGroup);
          BringItemToTop(pGroup,pItem, FALSE);
          // Handle updates.
          InvalidateRect(pGroup->hwnd,&pItem->rcTitle,TRUE);
          InvalidateRect(pGroup->hwnd,&pItemLast->rcTitle,TRUE);
          ViewActiveItem(pGroup);
          ret = TRUE;
        }
  
  
      GlobalUnlock(hStr);
      UnlockGroup(hwnd);
  
  #ifdef _DEBUG
  {
  TCHAR szDev[80];
  wsprintf ((LPTSTR)szDev,TEXT("IMEStringWindow: ret is %s \r\n"),ret);
  OutputDebugString((LPTSTR)szDev);
  }
  #endif
      return ret;
  }
  
  /****************************************************************************
   *
   *  IMEWindowGetCnt(LPSTR,LPSTR)
   *
   *  Compare strings from ahead and return the number of same character
   *
   *                         by yutakas 1992.10.22
   *
   *
   ****************************************************************************/
  int FAR PASCAL IMEWindowGetCnt(LPTSTR lp1, LPTSTR lp2)
  {
      int cnt = 0;
  
      while (*lp1 && *lp2)
      {
          // ToddB: This typecasting is to prevent lp1 and lp2 from being modified
          //      by CharUpper'ing one char at a time instead of the whole string
          if (CharUpper((LPTSTR)(DWORD)(BYTE)*lp1) ==
                  CharUpper((LPTSTR)(DWORD)(BYTE)*lp2))
          {
              cnt++;
          }
          else
              break;
  
          lp1++;
          lp2++;
      }
  
      return (*lp1 ? 0 : cnt);
  }
  