// ****************************************************************************
// OWL Extensions (OWLEXT) Class Library
// Copyright (C) 1998 by Dieter Windau
// All rights reserved
//
// bmpmenu.cpp: implementation file
// Version:     1.7
// Date:        04/24/1999
// Author:      Dieter Windau
//
// TBmpMenu is a freeware OWL class that shows bitmaps
// on the left side of menus like MS Office 97
//
// Portions of code are based on MFC class CMenuSpawn written by Iuri
// Apollonio and BCMenu written by Brent Corkum. Very special thanks.
//
// You are free to use/modify this code but leave this header intact.
// May not be sold for profit.
//
// The code was tested using Microsoft Visual C++ 6.0 SR2 with OWL6 patch 5
// and Borland C++ 5.02 with OWL 5.02. Both under Windows NT 4.0 SP4.
// This file is provided "as is" with no expressed or implied warranty.
// Use at your own risk.
//
// Many thanks to Riho Ellermaa, Michael Mogensen, J�rgen Welzenbach,
// Rob Beckers, Jo Parrello, Mark Hatsell an Yura Bidus for their help
// in testing and fixing the TBmpMenu classes.
//
// Please send me bug reports, bug fixes, enhancements, requests, etc., and
// I'll try to keep this in next versions:
//   EMail: dieter.windau@usa.net
//   Web:   http://www.members.aol.com/softengage/index.htm
// ****************************************************************************
#include <owlext\pch.h>
#pragma hdrstop
 
#if  !defined(OWL_UIHELPER_H)
#include <owl/uihelper.h>
#endif
#if  !defined(OWL_IMAGELST_H)
#include <owl/imagelst.h>
#endif
#if !defined(OWL_CELARRAY_H)
#include <owl/celarray.h>
#endif
#include <owl/appdict.h>
 
#include <owlext/bmpmenu.h>
#include <owlext/util.h>
 
const int DefaultBmpMenuWidth  = 19;  // if you use default borland size,
const int DefaultBmpMenuHeight = 18;  // change the values to 20 x 20
 
using namespace owl;
 
namespace OwlExt {
 
DIAG_DEFINE_GROUP_INIT(OWL_INI, BmpMenu, 1, 0);
 
 
 
//using namespace owl;
 
///////////////////////////////////////////////////////////
// Internall OWLExt function
// ~~~~~~~~~~~~~~~~~~~~~~~~~
TModule* FindResourceModule(TWindow* parent, TModule* module,
              TResId resId, LPCTSTR type);
 
 
// ********************* TBmpMenuItem *****************************************
 
bool TBmpMenuItem::operator ==(const TBmpMenuItem& d) const
{
  return memcmp(this, &d, sizeof(d)) == 0 ? true : false;
}
 
// ********************* TBmpMenuImageItem ************************************
 
TBmpMenuImageItem::TBmpMenuImageItem(int imageIdx, int cmd)
{
  ImageIdx = imageIdx;
  Cmd = cmd;
}
 
bool TBmpMenuImageItem::operator ==(const TBmpMenuImageItem& d) const
{
  return (ImageIdx == d.ImageIdx && Cmd == d.Cmd) ? true : false;
}
 
// *************************** TBmpMapEntry ***********************************
 
TBmpMapEntry::TBmpMapEntry(int commandId, int bmpId)
{
  CommandId = commandId;
  BmpId = bmpId;
}
 
bool TBmpMapEntry::operator ==(const TBmpMapEntry& d) const
{
  return (CommandId == d.CommandId && BmpId == d.BmpId) ? true : false;
}
 
// ******************** TBmpMenu *********************************************
 
TBmpMenu::TBmpMenu(TModule* module)
: // create and initialize
bShowAccInTooltips(true),
bDrawRecessed(true),
bIsPopup(false),
ImageSize(DefaultBmpMenuWidth-1, DefaultBmpMenuHeight-1),
ImageList(new TImageList(ImageSize, ILC_COLOR4|ILC_MASK, 0, 1)),
BackBitmap(0),
RadioBitmapId(0),
CheckBitmapId(0),
MenuFont(0),
MaskColor(TColor::LtGray),
Old3dFaceColor(TColor::Sys3dFace.Rgb()),
Module(module ? module : GetApplicationObject()),
MapEntries(new TIPtrArray<TBmpMapEntry*>),
ExcludedBitmaps(new TIntArray),
MenuItems(new TIPtrArray<TBmpMenuItem*>),
ImageItems(new TIPtrArray<TBmpMenuImageItem*>)
{
 
  // The size of the bitmaps on the left side are based on the
  // default bitmap size. Cut the right and bottom side, because
  // it's always gray. Change the constants at the top of
  // source, if you use default Borland size (20 x 20).
  //
 
  //  celArray = new TCelArray(ImageSize, ILC_COLOR4, 10, 5);
  ImageList->SetBkColor(Old3dFaceColor);
  //  celArray->SetBkColor(Old3dFaceColor);
 
  TRACEX(BmpMenu, 0, _T("TBmpMenu constructed @") << (void*)this);
}
 
TBmpMenu::~TBmpMenu()
{
  delete MapEntries;
  delete ExcludedBitmaps;
  delete MenuItems;
  delete ImageItems;
 
  ImageList->RemoveAll();
  delete ImageList;
  delete BackBitmap;
  delete MenuFont;
  //  delete celArray;
  TRACEX(BmpMenu, 0, "TBmpMenu destructed @" << (void*)this);
}
 
void TBmpMenu::RemapMenu(HMENU hMenu, bool isPopup)
// Remap a simple menu created with Windows API functions or with OWL TMenu
// class to a menu with bitmaps on the left side. Should be called from e.g:
// SetupWindow(), EvInitMenu(), EvInitMenuPopup() functions and before the
// TrackPopupMenu() function.
// If the hMenu is a popupMenu set isPopup = true
{
  PRECONDITION(hMenu);
  TRACEX(BmpMenu, 0, _T("RemapMenu @") << (void*)hMenu << _T(" isPopup=") << isPopup);
  SyncronizeAllMenuItems();
  if (::IsMenu(hMenu)){
    bIsPopup = isPopup;
    RemapMenuRecursiv(hMenu);
  }
}
 
#define DT_DRAW_OPTIONS (DT_VCENTER|DT_CENTER|DT_SINGLELINE)
bool TBmpMenu::DrawItem(DRAWITEMSTRUCT & DIS)
// To be called from the TWindow EvDrawItem(..)
// Call the base class if it return false
{
  TColor ocr, obkc;
  bool res = false;
  if (DIS.CtlType == ODT_MENU)
  {
    if (Old3dFaceColor.Rgb() != TColor::Sys3dFace.Rgb())
    {
      // if 3dFace color change, change the background of all bitmaps in list
      //
      Old3dFaceColor = TColor(TColor::Sys3dFace.Rgb());
      // celArray->SetBkColor(Old3dFaceColor);
      ImageList->SetBkColor(Old3dFaceColor);
    }
 
    UINT state = DIS.itemState;
    bool bEnab =  !(state & ODS_DISABLED);
    bool bSelect = (state & ODS_SELECTED) ? true : false;
    bool bChecked = (state & ODS_CHECKED) ? true : false;
    bool bItemInfoValid;
    bool bDefault;
    bool bRadioCheck;
 
    bItemInfoValid = bDefault = bRadioCheck = false;
    TBmpMenuItem* pItem = (TBmpMenuItem*)DIS.itemData;
    if (pItem)
    {
      // if it is not a submenu item, get the menu item info
      //
      TMenuItemInfo mii(MIIM_STATE | MIIM_TYPE);
      if (pItem->Cmd >= 0)
      {
        if ((bItemInfoValid = ::GetMenuItemInfo((HMENU)DIS.hwndItem, DIS.itemID,
          MF_BYCOMMAND, &mii)) == true)
        {
          if (mii.fState & MFS_DEFAULT)
            bDefault = true;
          if (mii.fType & MFT_RADIOCHECK)
            bRadioCheck = true;
        }
      }
 
      TDC dc(DIS.hDC);
 
      TFont* pft = NULL;
      if (!MenuFont){
        NONCLIENTMETRICS nm;
        nm.cbSize = sizeof (NONCLIENTMETRICS);
        if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize, &nm, 0)){
          // get the actual windows wide menu font
          if (bItemInfoValid && bDefault)
            nm.lfMenuFont.lfWeight = FW_BOLD;
          pft = new TFont(nm.lfMenuFont);
        }
        else
          pft = new TFont((HFONT)GetStockObject(DEFAULT_GUI_FONT));
        CHECK(pft);
        dc.SelectObject(*pft);
      }
      else
        dc.SelectObject(*MenuFont);
      int obk = dc.SetBkMode(TRANSPARENT);
 
      TRect rc(DIS.rcItem);
      TRect rcImage(rc);
      TRect rcText(rc);
      rcImage.right = rcImage.left + rc.Height();
 
      if (BackBitmap){
        // draw background bitmap
        //
        TMemoryDC tempDC(dc);
        tempDC.FillRect(rc, TBrush(TColor::SysMenu));
        tempDC.SelectObject(*BackBitmap);
        dc.FillRect(rc, TBrush(TColor::SysMenu));
 
        // draw bitmap tiled
        //
        int xBegin = (int)(rc.left/BackBitmap->Width());
        int xEnd   = (int)(rc.right/BackBitmap->Width())+1;
        int yBegin = (int)(rc.top/BackBitmap->Height());
        int yEnd   = (int)(rc.bottom/BackBitmap->Height())+1;
        int i,j;
        for (i = xBegin; i < xEnd; i++){
          for (j = yBegin; j < yEnd; j++) {
            tempDC.SetWindowOrg(TPoint(i*BackBitmap->Width(), j*BackBitmap->Height()));
            dc.BitBlt(rc, tempDC, rc.TopLeft(), SRCCOPY);
          }
        }
      }
 
      if (pItem->Cmd == -3) // is a separator
      {
        TPen pnDk(TColor::Sys3dShadow);
        TPen pnLt(TColor::Sys3dHilight);
        dc.SelectObject(pnDk);
        dc.MoveTo(rc.left + 2, rc.top + 2);
        dc.LineTo(rc.right - 2, rc.top + 2);
        dc.RestorePen();
        dc.SelectObject(pnLt);
        dc.MoveTo(rc.left + 2, rc.top + 3);
        dc.LineTo(rc.right - 2, rc.top + 3);
        dc.RestorePen();
      }
      else if (pItem->Cmd == -4){ // ia a title item
        owl::tstring cs(pItem->Text);
        TRect rcBdr(rcText);
 
        if (bDrawRecessed){
          // draw recessed title item
          //
          if (bSelect && bEnab){
            rcText.top ++;
            rcText.left += 2;
          }
 
          if (!BackBitmap)
            dc.FillRect(rcText, TBrush(TColor::SysMenu));
 
          if (bEnab) {
            ocr = dc.SetTextColor(TColor::SysMenuText);
            dc.DrawText(cs.c_str(), -1, rcText, DT_DRAW_OPTIONS);
          }
          else {
            rcText.Offset(1,1);
            ocr = dc.SetTextColor(TColor::Sys3dHilight);
            dc.DrawText(cs.c_str(), -1, rcText, DT_DRAW_OPTIONS);
            rcText.Offset(-1,-1);
            dc.SetTextColor(TColor::SysGrayText);
            dc.DrawText(cs.c_str(), -1, rcText, DT_DRAW_OPTIONS);
          }
          dc.SetTextColor(ocr);
 
          if (bSelect && bEnab)
            Draw3dRect(dc, rcBdr, TColor::Sys3dShadow, TColor::Sys3dHilight);
 
        }
        else {
          // draw normal title item
          //
          if (!BackBitmap)
            dc.FillRect(rcText, TBrush(TColor::SysMenu));
 
          if (bEnab) {
            if (bSelect) {
              ocr = dc.SetTextColor(TColor::SysHighlightText);
              obkc = dc.SetBkColor(TColor::SysHighlight);
              dc.FillRect(rcText, TBrush(TColor::SysHighlight));
            }
            else
              ocr = dc.SetTextColor(TColor::SysMenuText);
            dc.DrawText(cs.c_str(), -1, rcText, DT_DRAW_OPTIONS);
            if (bSelect)
              dc.SetBkColor(obkc);
          }
          else {
            rcText.Offset(1,1);
            dc.SetTextColor(TColor::Sys3dHilight);
            dc.DrawText(cs.c_str(), -1, rcText, DT_DRAW_OPTIONS);
            rcText.Offset(-1,-1);
            dc.SetTextColor(TColor::SysGrayText);
            dc.DrawText(cs.c_str(), -1, rcText, DT_DRAW_OPTIONS);
          }
          dc.SetTextColor(ocr);
        }
      }
      else {
        // draw menu item
        //
        rcText.left += rcImage.Width() + 1;
 
        if (bSelect){
          if (pItem->ImageIdx >= 0 || bChecked) {
            dc.FillRect(rcText, TBrush(TColor::SysHighlight));
            if (bChecked)
              dc.FillRect(rcImage, TBrush(TColor::SysMenu));
          }
          else
            dc.FillRect(rc, TBrush(TColor::SysHighlight));
          ocr = dc.SetTextColor(TColor::SysHighlightText);
        }
        else{
          if (pItem->ImageIdx >= 0 || bChecked){
            if (!BackBitmap)
              dc.FillRect(rcText, TBrush(TColor::SysMenu));
            if (bChecked && bEnab){
              // draw 50% mask
              //
              rcImage.Inflate(-1, -1);
              FillMaskRect(dc, rcImage);
              rcImage.Inflate(1, 1);
            }
          }
          else if (!BackBitmap)
            dc.FillRect(rc, TBrush(TColor::SysMenu));
          ocr = dc.SetTextColor(TColor::SysMenuText);
        }
 
        int ay = (rcImage.Height() - ImageSize.cy) / 2;
        int ax = (rcImage.Width()  - ImageSize.cx) / 2;
 
        if (pItem->ImageIdx >= 0){
          if (bSelect && bEnab){
            if (bChecked)
              Draw3dRect(dc, rcImage, TColor::Sys3dShadow, TColor::Sys3dHilight);
            else
              Draw3dRect(dc, rcImage, TColor::Sys3dHilight, TColor::Sys3dShadow);
          }
          else{
            if (bChecked)
              Draw3dRect(dc, rcImage, TColor::Sys3dShadow, TColor::Sys3dHilight);
            else
              Draw3dRect(dc, rcImage, TColor::SysMenu, TColor::SysMenu);
          }
 
          ImageList->Draw(pItem->ImageIdx, dc, rcImage.left + ax,
            rcImage.top + ay, (bChecked) ? ILD_TRANSPARENT : ILD_NORMAL);
 
          if (!bEnab){
            TRect strictImgRc(rcImage.left + ax, rcImage.top + ay,
              rcImage.left + ax + ImageSize.cx, rcImage.top + ay + ImageSize.cy);
            DrawDisabledButton(dc, strictImgRc);
          }
        }
        else
        {
          if (bChecked)
          {
            rcImage.Inflate(-1,-1);
            Draw3dRect(dc, rcImage, TColor::Sys3dShadow, TColor::Sys3dHilight);
 
            if (bItemInfoValid && bRadioCheck)
              DrawRadioDot(dc, rcImage.left + ax, rcImage.top + ay,
              bEnab, bSelect);
            else
              DrawCheckmark(dc, rcImage.left + ax, rcImage.top + ay,
              bEnab, bSelect);
          }
        }
 
        owl::tstring cs1;
        owl::tstring cs(pItem->Text);
        const auto sz = dc.GetTextExtentPoint32(cs);
        int ay1 = (rcText.Height() - sz.cy) / 2;
        rcText.top += ay1;
        rcText.left += 2;
        rcText.right -= 15;
 
        auto tf = cs.find(_T("\t"));
 
        // \a in resource is a tab too
        //
        // if (tf == cs.npos)
        //   tf = cs.find(_T("\a"));
 
        // Accelerator can be \b instead of \t (NOTE: \a in .rc make a \b in res)
        //
        if (tf == cs.npos)
          tf = cs.find(_T("\b"));
 
        if (tf != cs.npos){
          cs1 = cs.substr(tf+1);
          cs =  cs.substr(0, tf);
          if (!bEnab){
            if (!bSelect){
              TRect rcText1(rcText);
              rcText1.Inflate(-1,-1);
              dc.SetTextColor(TColor::Sys3dHilight);
              dc.DrawText(cs.c_str(),  -1, rcText1, DT_VCENTER|DT_LEFT);
              dc.DrawText(cs1.c_str(), -1, rcText1, DT_VCENTER|DT_RIGHT);
              dc.SetTextColor(TColor::SysGrayText);
              dc.DrawText(cs.c_str(),  -1, rcText, DT_VCENTER|DT_LEFT);
              dc.DrawText(cs1.c_str(), -1, rcText, DT_VCENTER|DT_RIGHT);
            }
            else{
              dc.SetTextColor(TColor::SysMenu);
              dc.DrawText(cs.c_str(),  -1, rcText, DT_VCENTER|DT_LEFT);
              dc.DrawText(cs1.c_str(), -1, rcText, DT_VCENTER|DT_RIGHT);
            }
          }
          else{
            dc.DrawText(cs.c_str(),  -1, rcText, DT_VCENTER|DT_LEFT);
            dc.DrawText(cs1.c_str(), -1, rcText, DT_VCENTER|DT_RIGHT);
          }
        }
        else{
          if (!bEnab){
            if (!bSelect){
              TRect rcText1(rcText);
              rcText1.Inflate(-1,-1);
              dc.SetTextColor(TColor::Sys3dHilight);
              dc.DrawText(cs.c_str(), -1,  rcText1, DT_VCENTER|DT_LEFT|DT_EXPANDTABS);
              dc.SetTextColor(TColor::SysGrayText);
              dc.DrawText(cs.c_str(), -1, rcText, DT_VCENTER|DT_LEFT|DT_EXPANDTABS);
            }
            else{
              dc.SetTextColor(TColor::SysMenu);
              dc.DrawText(cs.c_str(), -1,  rcText, DT_VCENTER|DT_LEFT|DT_EXPANDTABS);
            }
          }
          else
            dc.DrawText(cs.c_str(), -1, rcText, DT_VCENTER|DT_LEFT|DT_EXPANDTABS);
        }
        dc.SetTextColor(ocr);
      }
 
      dc.SetBkMode(obk);
      dc.RestoreFont();
      delete pft;
    }
    res = true;
  }
  return res;
}
 
bool TBmpMenu::MeasureItem(MEASUREITEMSTRUCT & MIS)
// To be called from the TWindow EvMeasureItem(..)
// Call the base class if it return false
{
  bool res = false;
  if (MIS.CtlType == ODT_MENU)
  {
    TBmpMenuItem* pItem = (TBmpMenuItem*)MIS.itemData;
    if (pItem)
    {
      if (pItem->Cmd == -3) // is a separator
      {
        MIS.itemWidth  = 10;
        MIS.itemHeight = 6;
      }
      else
      {
        owl::tstring cs(pItem->Text);
        if (cs != _T(""))
        {
          TClientDC dc(0);
 
          // get the actual windows wide menu font
          //
          TFont* pft = 0;
          if (MenuFont == 0)
          {
            NONCLIENTMETRICS nm;
            nm.cbSize = sizeof (NONCLIENTMETRICS);
            if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize, &nm, 0))
              pft = new TFont(nm.lfMenuFont);
            else
              pft = new TDefaultGUIFont();
            CHECK(pft);
            dc.SelectObject(*pft);
          }
          else
            dc.SelectObject(*MenuFont);
          if (pItem->Cmd == -4) // is a title item
          {
            TRect rci(0,0,0,0);
            dc.DrawText(cs.c_str(), -1, rci, DT_CALCRECT|DT_TOP|DT_VCENTER|DT_SINGLELINE);
            MIS.itemHeight = rci.Height();
            MIS.itemWidth = rci.Width();
          }
          else
          {
            int h = GetSystemMetrics(SM_CYMENU);
            MIS.itemHeight = h>ImageSize.cy+3 ? h : ImageSize.cy+3;
 
            TSize sz = dc.GetTabbedTextExtent(cs, static_cast<int>(cs.size()), 0, 0);
            if (MIS.itemHeight < static_cast<uint>(sz.cy + 3))
              MIS.itemHeight = sz.cy + 3;
            MIS.itemWidth = MIS.itemHeight + 2 + sz.cx;
 
            // make the menu bigger, because sometimes the calculated
            // width is not enough
            //
            MIS.itemWidth += 2 * (sz.cx / static_cast<uint>(cs.size()));
          }
 
          dc.RestoreFont();
          delete pft;
        }
        else
        {
          MIS.itemHeight = ImageSize.cy + 3;
          MIS.itemWidth  = 100;
        }
      }
    }
    res = true;
  }
  return res;
}
 
bool TBmpMenu::FindKeyboardShortcut(UINT nChar, UINT, HMENU hMenu, LRESULT& lRes)
// To be called from the TWindow EvMenuChar(..)
// Call the base class if it return false
{
  PRECONDITION(hMenu);
  int nItem = ::GetMenuItemCount(hMenu);
  owl::tstring csChar;
  csChar = (_TCHAR)nChar;
  _tcslwr((_TCHAR*)csChar.c_str());
  while ((--nItem)>=0){
    UINT itemId = ::GetMenuItemID(hMenu, nItem);
    if (itemId != 0){
      TMenuItemInfo mii(MIIM_DATA|MIIM_TYPE);
      if (::GetMenuItemInfo(hMenu, nItem, TRUE, &mii)){
        if (mii.fType&MFT_OWNERDRAW){
          TBmpMenuItem* pItem = (TBmpMenuItem*)mii.dwItemData;
          if (pItem){
            owl::tstring csItem(pItem->Text);
            _tcslwr((_TCHAR*)csItem.c_str());
            const auto iAmperIdx = csItem.find(_T("&"));
            if (iAmperIdx != csItem.npos)
            {
              csItem = csItem.substr(iAmperIdx + 1, 1);
              if (csItem == csChar){
                lRes = MAKELONG((uint16)nItem, 2);
                return true;
              }
            }
          }
        }
      }
    }
  }
  return false;
}
 
int TBmpMenu::PreGetHintText(HMENU hmenu, uint id, LPTSTR buf, int size,
               THintText hintType)
               // Retrieves the hint text associated with a particular Id
               // To be called from DecoratedFrame GetHintText(...)
               // Call the base class if it return 0.
{
  int numBytes = 0;
 
  if (hintType == htTooltip){
    if (hmenu && ::IsMenu(hmenu)) {
      // For tooltips, we first look if the same id exists on the
      // bitmap menu and use the associated menu string.
      //
      bool found = false;
      bool accValid = true;
      for (uint j = 0; j < MenuItems->GetItemsInContainer(); j++) {
        if ((*MenuItems)[j]->Cmd == (int)id){
          if (_tcslen((*MenuItems)[j]->Text) < (uint)size)
            _tcscpy(buf, (*MenuItems)[j]->Text);
          else
          {
            _tcsncpy(buf, (*MenuItems)[j]->Text, size-1);
            buf[size-1] = 0;
            accValid = false;
          }
          found = true;
          break;
        }
      }
      if (found == false)
        return numBytes;
 
      // Trim accelerator text e.g., '\tCtrl+X' (if any)
      // Accelerator can be \b instead of \t (NOTE: \a in .rc make a \b in res)
      //
      LPTSTR acc = new TCHAR[size];
      *acc = 0;
      LPTSTR c = _tcschr(buf, _T('\t'));
      if (c == 0)
        c = _tcschr(buf, _T('\b'));
      if (c)
      {
        if (bShowAccInTooltips && accValid)
        {
          // save the accelerator string for showing in tooltips
          //
          _tcscpy(acc, c+1);
        }
        *c = 0;
      }
 
      // Trim trailing dots, e.g., '...' (if any)
      //
      int i;
      for (i = static_cast<int>(_tcslen(buf)) - 1; i >= 0; i--)
        if (buf[i] == _T('.'))
          buf[i] = 0;
        else
          break;
 
      // Eliminate '&' (just emliminate first one)
      //
      i = -1;
      while(buf[++i])
        if (buf[i] == _T('&')) {
          do {
            buf[i] = buf[i+1];
            i++;
          } while (buf[i]);
          break;
        }
 
        // Show the accelerator in tooltip
        //
        if (bShowAccInTooltips && accValid && *acc != 0 &&
          (_tcslen(buf) + _tcslen(acc) + 3) < (uint)size)
        {
          _tcscat(buf, _T(" ("));
          _tcscat(buf, acc);
          _tcscat(buf, _T(")"));
        }
 
        delete[] acc;
 
        numBytes = static_cast<int>(_tcslen(buf));
    }
  }
  return numBytes;
}
 
bool TBmpMenu::IsDrawRecessed()
{
  return bDrawRecessed;
}
 
void TBmpMenu::SetDrawRecessed(bool b)
{
  bDrawRecessed = b;
}
 
bool TBmpMenu::IsWithAccel()
{
  return bShowAccInTooltips;
}
 
void TBmpMenu::SetWithAccel(bool b)
{
  bShowAccInTooltips = b;
}
 
bool TBmpMenu::IsBackBitmap()
{
  return BackBitmap !=0;
}
 
bool TBmpMenu::SetBackBitmap(uint resId)
// Set the background bitmap. If resId == 0 all menus will be show normal.
// return true if successful
{
  PRECONDITION(Module);
 
  delete BackBitmap;
  BackBitmap = 0;
 
  if (resId && resId != (uint)-1) {
    // 1: module, 2: Application module, 3: OWLExt module, 4 OWL Module
    TModule* module = FindResourceModule(0,Module,resId,RT_BITMAP);
    if (module)
      BackBitmap = new TBitmap(*module, TResId(resId));
  }
  return BackBitmap!=0;
}
 
bool TBmpMenu::IsRadioBitmap()
{
  return RadioBitmapId!=0;
}
 
bool TBmpMenu::SetRadioBitmap(uint resId)
// Set the radio dot bitmap. If resId == 0 a default radio dot will be draw.
// return true if successful
{
  PRECONDITION(Module);
  RadioBitmapId = 0;
  if (resId){
    TModule* module = FindResourceModule(0,Module,resId,RT_BITMAP);
    if (module)
      RadioBitmapId = resId;
  }
  return RadioBitmapId != 0;
}
 
bool TBmpMenu::IsCheckBitmap()
{
  return CheckBitmapId!=0;
}
 
bool TBmpMenu::SetCheckBitmap(uint resId)
// Set the checkbox bitmap. If resId == 0 a default checkbox will be draw.
// return true if successful
{
  PRECONDITION(Module);
 
  CheckBitmapId = 0;
  if (resId){
    TModule* module = FindResourceModule(0,Module,resId,RT_BITMAP);
    if (module)
      CheckBitmapId = resId;
  }
  return CheckBitmapId!=0;
}
 
//
TFont* TBmpMenu::GetMenuFont()
{
  return MenuFont;
}
 
void TBmpMenu::SetMenuFont(TFont* menuFont)
// Get/Set the menu font. The font will be deleted in this class
// By default the windows font for menus is used
{
  delete MenuFont;
  MenuFont = 0;
  MenuFont = menuFont;
}
 
TColor TBmpMenu::GetMaskColor()
{
  return MaskColor;
}
 
void TBmpMenu::SetMaskColor(const TColor& maskColor)
{
  MaskColor = maskColor;
}
//
void TBmpMenu::ExcludeBitmap(int resId)
// Exclude the bitmap with resId. It will not be shown in menus
{
  if (ExcludedBitmaps->Find(resId) == ExcludedBitmaps->NPOS)
    ExcludedBitmaps->Add(resId);
}
 
void TBmpMenu::IncludeBitmap(int resId)
// Include the bitmap with resId. By default all bitmaps in module
// will be shown in menus
{
  int idx = ExcludedBitmaps->Find(resId);
  if (idx != ExcludedBitmaps->NPOS)
    ExcludedBitmaps->Destroy(idx);
}
 
int TBmpMenu::AddMapEntry(TBmpMapEntry* entry)
// Adds a entry into the array MapEntries
{
  PRECONDITION(entry);
  return MapEntries->Add(entry);
}
 
void TBmpMenu::RemoveMapEntry(int loc)
// Removes entry at loc, and reduces the array by one element
{
  MapEntries->RemoveEntry(loc);
}
 
bool TBmpMenu::SetMenuItemText(HMENU hMenu, UINT pos, LPCTSTR txt)
// Set the owner draw menu item text like Win API function:
//   ModifyMenu(hMenu, pos, MF_BYPOSITION | MF_STRING, .., txt)
{
  PRECONDITION(hMenu);
  TCHAR str[MaxMenuStringLen];
  _tcscpy(str, txt ? txt : _T(""));
 
  TMenuItemInfo mii(MIIM_TYPE | MIIM_DATA | MIIM_STATE | MIIM_ID | MIIM_SUBMENU);
  if (::GetMenuItemInfo(hMenu, pos, true, &mii)){
    if (mii.fType&MFT_OWNERDRAW && mii.dwItemData != 0){
      // search menu item and change the text
      //
      for (int i=0; i < (int)MenuItems->GetItemsInContainer(); i++){
        if ((*MenuItems)[i]->Menu == hMenu && (*MenuItems)[i]->Pos == pos){
          if ((*MenuItems)[i]->Cmd > 0){
            if ((UINT)(*MenuItems)[i]->Cmd == mii.wID){
              if (_tcscmp ((*MenuItems)[i]->Text, str) == 0)
                return true; // nothing to change
              _tcscpy((*MenuItems)[i]->Text, str);
              return ::ModifyMenu(hMenu, pos, MF_BYPOSITION|MF_OWNERDRAW|mii.fState,
                (LPARAM)mii.wID, (LPCTSTR)(*MenuItems)[i]);
            }
          }
          else{
            if (mii.hSubMenu != 0){
              if (_tcscmp ((*MenuItems)[i]->Text, str) == 0)
                return true; // nothing to change
              _tcscpy((*MenuItems)[i]->Text, str);
              return ::ModifyMenu(hMenu, pos, MF_BYPOSITION|MF_OWNERDRAW|mii.fState,
                (LPARAM)-1, (LPCTSTR)(*MenuItems)[i]);
            }
          }
        }
      }
    }
  }
  return false;
}
 
void TBmpMenu::RemapMenuRecursiv(HMENU hMenu)
{
  static int iRecurse = 0;
  iRecurse ++;
 
  int nItem = ::GetMenuItemCount(hMenu);
  while ((--nItem)>=0)
  {
    UINT itemId = ::GetMenuItemID(hMenu, nItem);
    if (itemId == (UINT) -1)
    {
      HMENU pops = ::GetSubMenu(hMenu, nItem);
      if (pops)
        RemapMenuRecursiv(pops);
      if (bIsPopup || iRecurse>0)
      {
        TCHAR cs[MaxMenuStringLen];
        if (::GetMenuString(hMenu, nItem, cs, MaxMenuStringLen, MF_BYPOSITION) > 0)
        {
          TBmpMenuItem* pItem = AddMenuItem(hMenu, cs,
            (bIsPopup == false && iRecurse == 1)?-4:-2, nItem);
          ::ModifyMenu(hMenu, nItem, MF_BYPOSITION|MF_OWNERDRAW,
            (UINT)-1, (LPCTSTR)pItem);
        }
      }
    }
    else
    {
      TBmpMenuItem* pItem;
      TCHAR cs[MaxMenuStringLen];
      UINT oldState = ::GetMenuState(hMenu, nItem, MF_BYPOSITION);
      if (!(oldState&MF_OWNERDRAW) && !(oldState&MF_BITMAP))
      {
        if ((oldState&MF_SEPARATOR) || itemId == 0)
        {
          // Insert separator items only if a background bitmap is drawn
          //
          if (BackBitmap){
            pItem = AddMenuItem(hMenu, _T(""), -3, nItem);
            ::ModifyMenu(hMenu, nItem, MF_BYPOSITION|MF_OWNERDRAW|oldState,
              (LPARAM)itemId, (LPCTSTR)pItem);
          }
        }
        else
        {
          if (::GetMenuString(hMenu, nItem, cs, MaxMenuStringLen, MF_BYPOSITION) > 0)
          {
            TryToLoadBmpResource(itemId);
            pItem = AddMenuItem(hMenu, cs, itemId, nItem);
            ::ModifyMenu(hMenu, nItem, MF_BYPOSITION|MF_OWNERDRAW|oldState,
              (LPARAM)itemId, (LPCTSTR)pItem);
          }
        }
      }
    }
  }
  iRecurse --;
}
 
void TBmpMenu::DrawCheckmark(TDC& dc, int x, int y, bool enable, bool select)
{
  if (CheckBitmapId){
 
    TPointer<TDib> bitmap(new TDib(*Module, TResId(CheckBitmapId)));
 
    bitmap->MapUIColors(TDib::MapText|TDib::MapShadow|TDib::MapHighlight);
    bitmap->MapColor(MaskColor, TColor::Sys3dFace);
 
    TCelArray celArray(*(TDib*)bitmap, 1);
 
    TUIFace uiFace(TRect(TPoint(x, y), TSize(bitmap->Width(),
      bitmap->Height())), celArray);
    if (enable)
      uiFace.Paint(dc, TPoint(0, 0), select ? TUIFace::Normal :
      TUIFace::Down, true);
    else
      uiFace.Paint(dc, TPoint(0, 0), TUIFace::Disabled, false);
  }
  else  {
    // ......X
    // .....XX
    // X...XXX
    // XX.XXXo
    // XXXXXo.
    // .XXXo..
    // ..Xo...
    // ..o....
    x += 4;
    y += 4;
 
    TColor color = (enable) ? TColor::SysMenuText : TColor::Sys3dShadow;
 
    dc.SetPixel(x,y+2,color);
    dc.SetPixel(x,y+3,color);
    dc.SetPixel(x,y+4,color);
    dc.SetPixel(x+1,y+3,color);
    dc.SetPixel(x+1,y+4,color);
    dc.SetPixel(x+1,y+5,color);
    dc.SetPixel(x+2,y+4,color);
    dc.SetPixel(x+2,y+5,color);
    dc.SetPixel(x+2,y+6,color);
    dc.SetPixel(x+3,y+3,color);
    dc.SetPixel(x+3,y+4,color);
    dc.SetPixel(x+3,y+5,color);
    dc.SetPixel(x+4,y+2,color);
    dc.SetPixel(x+4,y+3,color);
    dc.SetPixel(x+4,y+4,color);
    dc.SetPixel(x+5,y+1,color);
    dc.SetPixel(x+5,y+2,color);
    dc.SetPixel(x+5,y+3,color);
    dc.SetPixel(x+6,y,color);
    dc.SetPixel(x+6,y+1,color);
    dc.SetPixel(x+6,y+2,color);
    if (enable == false)
    {
      TColor shadowcolor = TColor::Sys3dHilight;
      dc.SetPixel(x+2,y+7,shadowcolor);
      dc.SetPixel(x+3,y+6,shadowcolor);
      dc.SetPixel(x+4,y+5,shadowcolor);
      dc.SetPixel(x+5,y+4,shadowcolor);
      dc.SetPixel(x+6,y+3,shadowcolor);
    }
  }
}
 
//
void TBmpMenu::DrawRadioDot(TDC& dc, int x, int y, bool enable, bool select)
{
  if (RadioBitmapId){
 
    TPointer<TDib> bitmap(new TDib(*Module, TResId(RadioBitmapId)));
 
    bitmap->MapUIColors(TDib::MapText|TDib::MapShadow|TDib::MapHighlight);
    bitmap->MapColor(MaskColor, TColor::Sys3dFace);
 
    TCelArray celArray(*(TDib*)bitmap, 1);
 
    TUIFace uiFace(TRect(TPoint(x, y), TSize(bitmap->Width(),
      bitmap->Height())), celArray);
    if (enable)
      uiFace.Paint(dc, TPoint(0, 0), select ? TUIFace::Normal :
      TUIFace::Down, true);
    else
      uiFace.Paint(dc, TPoint(0, 0), TUIFace::Disabled, false);
  }
  else
  {
    x += 4;
    y += 4;
    TColor color = enable ? TColor::SysMenuText : TColor::Sys3dShadow;
    if (enable == false) {
      TRect rcDot(x, y, x+8, y+8);
      TBrush brush(TColor::Sys3dHilight);
      TPen pen(TColor::Sys3dHilight,0);
      dc.SelectObject(brush);
      dc.SelectObject(pen);
      dc.Ellipse(rcDot);
      dc.RestoreBrush();
      dc.RestorePen();
    }
    TRect rcDot(x, y, x+7, y+7);
    TBrush brush(color);
    TPen pen(color,0);
    dc.SelectObject(brush);
    dc.SelectObject(pen);
    dc.Ellipse(rcDot);
    dc.RestoreBrush();
    dc.RestorePen();
  }
}
 
//
int TBmpMenu::FindImageItem(int cmd)
{
  for (uint i = 0; i < ImageItems->Size(); i++)
    if ((*ImageItems)[i]->Cmd == cmd)
      return (*ImageItems)[i]->ImageIdx;
 
  return -1;
}
//
TBmpMenuItem* TBmpMenu::AddMenuItem(HMENU hMenu, LPCTSTR txt, int cmd, uint pos)
{
  PRECONDITION(hMenu);
 
  TCHAR str[MaxMenuStringLen];
  if (txt)
    _tcscpy(str, txt);
  else
    _tcscpy(str, _T(""));
 
  // if the same item exist, do not insert the menu Item
  //
  for (int i=0; i<(int)MenuItems->GetItemsInContainer(); i++)
  {
    if ((*MenuItems)[i]->Menu == hMenu &&
      (*MenuItems)[i]->Cmd == cmd &&
      (*MenuItems)[i]->Pos == pos &&
      _tcscmp((*MenuItems)[i]->Text, txt) == 0)
      return (*MenuItems)[i];
  }
 
  TBmpMenuItem* pItem = new TBmpMenuItem;
  //CHECK(pItem); will throw if error
 
  // set the menu item data
  //
  pItem->Menu = hMenu;
  pItem->Pos = pos;
  pItem->Cmd = cmd;
  if (cmd >= 0)
    pItem->ImageIdx = FindImageItem(cmd);
  else
    pItem->ImageIdx = cmd;
 
  _tcscpy(pItem->Text, str);
 
  MenuItems->Add(pItem);
  return pItem;
}
//
void TBmpMenu::AddImageItem(int idx, WORD cmd)
{
  if (idx < 0)
    return;
 
  ImageItems->Add(new TBmpMenuImageItem(idx, cmd));
}
//
bool TBmpMenu::TryToLoadBmpResource(unsigned int resId)
{
  PRECONDITION(Module);
  unsigned int mapResId = resId;
 
  // Check if the command id is mapped to another bitmap id
  //
  for (int i=0; i < (int)MapEntries->GetItemsInContainer(); i++) {
    if ((*MapEntries)[i]->CommandId == (int)resId){
      mapResId = (*MapEntries)[i]->BmpId;
      break;
    }
  }
 
  // Check if the bitmap is excluded
  // If a bitmap is exculuded, that is associated to more commands,
  // all commands are not shown with bitmap
  //
  if (ExcludedBitmaps->Find(mapResId) != ExcludedBitmaps->NPOS)
    return false;
 
  // Add only one bitmap for each command, because a command can be
  // in more than one menu
  //
  if (FindImageItem(resId) < 0){
    // Load bitmap from module and add them to image list
    //
    TModule* module = FindResourceModule(0,Module,mapResId,RT_BITMAP);
    if (module){
      int nBmpItems = ImageList->Add(TBitmap(*module, TResId(mapResId)), MaskColor);
      //int nBmpItems = celArray->Add(srcBmp);
      AddImageItem(nBmpItems, (WORD)resId);
      return true;
    }
  }
  return false;
}
 
void TBmpMenu::SyncronizeAllMenuItems()
// Syncronize the intern menu items and the real windows menus and items
{
  UINT i, num=MenuItems->GetItemsInContainer();
  TBmpMenuItem* item;
  for (i=num; i>0; i--)
  {
    // Delete a intern menu item, if the hMenu member
    // is not valid (e.g. the associated menu is destroyed).
    //
    item = (*MenuItems)[i-1];
    if (::IsMenu(item->Menu) == false)
      MenuItems->Destroy(i-1);
  }
 
  num=MenuItems->GetItemsInContainer();
  for (i=num; i>0; i--)
  {
    bool bRet, bDestroy = false;
    TMenuItemInfo mii(MIIM_TYPE | MIIM_DATA | MIIM_STATE | MIIM_ID | MIIM_SUBMENU);
    item = (*MenuItems)[i-1];
    if (item->Cmd > 0)
      bRet = ::GetMenuItemInfo(item->Menu, item->Cmd, false, &mii);
    else
      bRet = ::GetMenuItemInfo(item->Menu, item->Pos, true, &mii);
    if (bRet == false)
    {
      // if windows menu item not present (e.g. removed), delete intern item
      //
      MenuItems->Destroy(i-1);
    }
    else
    {
      if (item->Cmd > 0) // intern item is a simple item
      {
        if (mii.hSubMenu != 0)
        {
          // if windows menu item is a submenu, delete intern item
          //
          bDestroy = true;
        }
        else if ((UINT)item->Cmd != mii.wID)
        {
          // if windows command change, delete intern item
          //
          bDestroy = true;
        }
      }
      else
      {
        if (item->Cmd == -3)
        {
          // if intern item is a separator and there is no background bitmap,
          // delete intern item
          //
          if (!BackBitmap)
            bDestroy = true;
        }
        else if (mii.hSubMenu == 0)
        {
          // if intern item is a submenu and windows menu is a simple item,
          // delete intern item
          //
          bDestroy = true;
        }
      }
 
      if (bDestroy == false &&
        !(mii.fType&MFT_OWNERDRAW)) // windows item is not ownerdrawn
      {
        if ((mii.fType&MFT_STRING) && mii.dwTypeData != 0)
        {
          // if windows item is a string item, try to syncronize the items
          //
          _tcscpy(item->Text, mii.dwTypeData);
          uint cmd = (item->Cmd <= 0) ? (UINT)-1 : item->Cmd;
          ::ModifyMenu(item->Menu, item->Pos,
            MF_BYPOSITION|MF_OWNERDRAW|mii.fState, cmd, (LPCTSTR)item);
        }
      }
 
      // if windows item is ownerdrawn and have no valid datas, delete itern item
      //
      if ((mii.fType&MFT_OWNERDRAW) && mii.dwItemData == 0)
        bDestroy = true;
 
      // check menu type before destroy the intern item
      //
      if (bDestroy && !(mii.fType&MFT_OWNERDRAW))
        MenuItems->Destroy(i-1);
    }
  }
}
 
} // OwlExt namespace
//
 

V560 A part of conditional expression is always false: (mii.fType & 0x00000000L).

V817 It is more efficient to seek '\t' character rather than a string.

V817 It is more efficient to seek '\x08' character rather than a string.

V817 It is more efficient to seek '&' character rather than a string.

V815 Decreased performance. Consider replacing the expression 'cs != ""' with '!cs.empty()'.

V821 Decreased performance. The 'rcBdr' variable can be constructed in a lower level scope.