root / trunk / win / Bmpmenu.cpp

Revision 654, 41.3 kB (checked in by leo, 2 years ago)

Added precompiled headers to all source files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// BMPMenu.cpp : implementation file
2//
3// Based on Brent Corkum's BCMenu 2.3 class from
4// http://www.rocscience.com/~corkum/BCMenu.html
5//
6
7#include "lc_global.h"
8#include "BMPMenu.h"
9#include <afxpriv.h> // makes A2W and other spiffy AFX macros work
10#include "resource.h"
11
12#ifdef _DEBUG
13#define new DEBUG_NEW
14#undef THIS_FILE
15static char THIS_FILE[] = __FILE__;
16#endif
17
18#define GAP 1
19#ifndef OBM_CHECK
20#define OBM_CHECK 32760 // from winuser.h
21#endif
22
23static CPINFO CPInfo;
24
25typedef enum Win32Type {
26    Win32s,
27    Windoze95,
28    WinNT3,
29    WinNT4orHigher
30};
31
32static Win32Type IsShellType()
33{
34    Win32Type ShellType;
35    DWORD winVer;
36    OSVERSIONINFO *osvi;
37   
38    winVer = GetVersion();
39    if(winVer < 0x80000000)
40    {
41        ShellType=WinNT3;
42        osvi = (OSVERSIONINFO*)malloc(sizeof(OSVERSIONINFO));
43        if (osvi != NULL)
44        {
45            memset(osvi, 0, sizeof(OSVERSIONINFO));
46            osvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
47            GetVersionEx(osvi);
48            if (osvi->dwMajorVersion >= 4L)
49                ShellType = WinNT4orHigher; //yup, it is NT 4 or higher!
50            free(osvi);
51        }
52    }
53    else if (LOBYTE(LOWORD(winVer)) < 4)
54        ShellType=Win32s;
55    else
56        ShellType=Windoze95;
57    return ShellType;
58}
59
60static Win32Type g_Shell = IsShellType();
61
62void CBMPMenuData::SetAnsiString(LPCSTR szAnsiString)
63{
64    ASSERT(szAnsiString);
65    USES_CONVERSION;
66    SetWideString(A2W(szAnsiString)); // see MFC Tech Note 059
67}
68
69// Returns the menu text in ANSI or UNICODE
70CString CBMPMenuData::GetString(void)
71{
72    CString strText;
73    if (m_szMenuText)
74    {
75#ifdef UNICODE
76        strText = m_szMenuText;
77#else
78        USES_CONVERSION;
79        strText=W2A(m_szMenuText); // see MFC Tech Note 059
80#endif   
81    }
82    return strText;
83}
84
85/////////////////////////////////////////////////////////////////////////////
86// CBMPMenu construction/destruction
87
88CBMPMenu::CBMPMenu()
89{
90    disable_old_style=FALSE;
91    m_iconX = 16; // Icon sizes default to 16 x 16
92    m_iconY = 15;
93    m_selectcheck = -1;
94    m_unselectcheck = -1;
95    checkmaps=NULL;
96    checkmapsshare=FALSE;
97    // set the color used for the transparent background in all bitmaps
98    m_bitmapBackground = RGB(192,192,192); //gray
99    m_bitmapBackgroundFlag=FALSE;
100    GetCPInfo(CP_ACP,&CPInfo);
101    if (!m_List.m_hImageList)
102        m_List.Create (IDB_VIEWPORTS, 41, 0, RGB (192,192,192));
103}
104
105CBMPMenu::~CBMPMenu()
106{
107    DestroyMenu();
108}
109
110BOOL CBMPMenu::IsNewShell ()
111{
112    return (Windoze95==g_Shell || WinNT4orHigher==g_Shell);
113}
114
115
116CBMPMenuData::~CBMPMenuData()
117{
118    if(bitmap)
119        delete(bitmap);
120
121    delete[] m_szMenuText; // Need not check for NULL because ANSI X3J16 allows "delete NULL"
122}
123
124void CBMPMenuData::SetWideString(const wchar_t *szWideString)
125{
126    delete[] m_szMenuText; // Need not check for NULL because ANSI X3J16 allows "delete NULL"
127   
128    if (szWideString)
129    {
130        m_szMenuText = new wchar_t[sizeof(wchar_t)*(wcslen(szWideString)+1)];
131        if (m_szMenuText)
132            wcscpy(m_szMenuText,szWideString);
133    }
134    else
135        m_szMenuText = NULL;
136}
137
138BOOL CBMPMenu::IsMenu(CMenu *submenu)
139{
140    int m;
141   
142    int numSubMenus = m_SubMenus.GetUpperBound();
143    for(m = 0; m <= numSubMenus; ++m)
144        if(submenu == m_SubMenus[m])
145            return TRUE;
146    return FALSE;
147}
148
149BOOL CBMPMenu::DestroyMenu()
150{
151    int m;
152
153    // Destroy Sub menus:
154    int numSubMenus = m_SubMenus.GetUpperBound();
155    for(m = numSubMenus; m >= 0; m--)
156        delete m_SubMenus[m];
157    m_SubMenus.RemoveAll();
158
159    // Destroy menu data
160    int numItems = m_MenuList.GetUpperBound();
161    for(m = 0; m <= numItems; m++)
162        delete m_MenuList[m];
163    m_MenuList.RemoveAll();
164    if(checkmaps && !checkmapsshare)
165    {
166        delete checkmaps;
167        checkmaps=NULL;
168    }
169
170    return CMenu::DestroyMenu();
171};
172
173
174///////////////////////////////////////////////////////////////////////////
175//
176// CBMPMenu message handlers
177
178// Called by the framework when a particular item needs to be drawn.
179void CBMPMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
180{
181    ASSERT(lpDIS != NULL);
182    CDC* pDC = CDC::FromHandle(lpDIS->hDC);
183
184    UINT u = ((CBMPMenuData*)(lpDIS->itemData))->nID;
185    if (u >= ID_VIEWPORT01 && u <= ID_VIEWPORT14)
186    {
187        int left = lpDIS->rcItem.left;
188
189        if (lpDIS->itemAction & ODA_DRAWENTIRE)
190            m_List.Draw (pDC, u - ID_VIEWPORT01, CPoint (left+3,lpDIS->rcItem.top+1), ILD_NORMAL);
191
192        if ((lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
193            m_List.Draw (pDC, u - ID_VIEWPORT01, CPoint (left+3,lpDIS->rcItem.top+1), ILD_SELECTED);
194
195        if (!(lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & ODA_SELECT))
196            m_List.Draw (pDC, u - ID_VIEWPORT01, CPoint (left+3,lpDIS->rcItem.top+1), ILD_NORMAL);
197
198        if (lpDIS->itemState & ODS_CHECKED)
199        {
200            CBrush br(RGB(0,0,0));
201            pDC->FrameRect(CRect (left+2,lpDIS->rcItem.top,left+41+4,lpDIS->rcItem.bottom), &br);
202        }
203        return;
204    }
205
206    CRect rect;
207    UINT state = (((CBMPMenuData*)(lpDIS->itemData))->nFlags);
208    if(state & MF_SEPARATOR)
209    {
210        rect.CopyRect(&lpDIS->rcItem);
211        rect.top+=rect.Height()>>1;
212        pDC->DrawEdge(&rect,EDGE_ETCHED,BF_TOP);
213    }
214    else
215    {
216        CRect rect2;
217        BOOL standardflag=FALSE,selectedflag=FALSE,disableflag=FALSE;
218        BOOL checkflag=FALSE;
219        CBitmap bitmapstandard;
220        COLORREF crText = GetSysColor(COLOR_MENUTEXT);
221        COLORREF m_clrBack=GetSysColor(COLOR_MENU);
222        CBrush m_brBackground,m_brSelect;
223        CPen m_penBack;
224        int x0,y0,dy;
225        int nIconNormal=-1,xoffset=-1;
226        CImageList *bitmap=NULL;
227        CFont m_fontMenu;
228        LOGFONT m_lf;
229       
230        // set some colors and the font
231        m_penBack.CreatePen (PS_SOLID,0,GetSysColor(COLOR_MENU));
232        m_brBackground.CreateSolidBrush(GetSysColor(COLOR_MENU));
233        m_brSelect.CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
234        ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
235        NONCLIENTMETRICS nm;
236        nm.cbSize = sizeof (NONCLIENTMETRICS);
237        VERIFY (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,nm.cbSize,&nm,0));
238        m_lf =  nm.lfMenuFont;
239        m_fontMenu.CreateFontIndirect (&m_lf);
240       
241        // draw the colored rectangle portion
242        rect.CopyRect(&lpDIS->rcItem);
243        rect2=rect;
244       
245        // draw the up/down/focused/disabled state
246        UINT action = lpDIS->itemAction;
247        UINT state = lpDIS->itemState;
248        CString strText;
249        LOGFONT lf;
250        lf = m_lf;
251       
252        CFont dispFont;
253        CFont *pFont;
254       
255        if(lpDIS->itemData != NULL)
256        {
257            nIconNormal = (((CBMPMenuData*)(lpDIS->itemData))->menuIconNormal);
258            xoffset = (((CBMPMenuData*)(lpDIS->itemData))->xoffset);
259            bitmap = (((CBMPMenuData*)(lpDIS->itemData))->bitmap);
260            strText = ((CBMPMenuData*) (lpDIS->itemData))->GetString();
261           
262            if(state&ODS_CHECKED && nIconNormal<0)
263            {
264                if(state&ODS_SELECTED && m_selectcheck>0)checkflag=TRUE;
265                else if(m_unselectcheck>0) checkflag=TRUE;
266            }
267            else if(nIconNormal != -1)
268            {
269                standardflag = TRUE;
270                if(state & ODS_SELECTED && !(state & ODS_GRAYED))
271                    selectedflag = TRUE;
272                else if(state & ODS_GRAYED)
273                    disableflag = TRUE;
274            }
275        }
276        else
277            strText.Empty();
278       
279        if(state&ODS_SELECTED) // draw the down edges
280        {
281            CPen *pOldPen = pDC->SelectObject (&m_penBack);
282           
283            // You need only Text highlight and thats what you get
284            if(checkflag||standardflag||selectedflag||disableflag||state&ODS_CHECKED)
285                rect2.SetRect(rect.left+m_iconX+4+GAP,rect.top,rect.right,rect.bottom);
286            pDC->FillRect (rect2,&m_brSelect);
287           
288            pDC->SelectObject (pOldPen);
289            if((HFONT)dispFont != NULL)dispFont.DeleteObject ();
290            dispFont.CreateFontIndirect (&lf);
291            crText = GetSysColor(COLOR_HIGHLIGHTTEXT);
292        }
293        else 
294        {
295            CPen *pOldPen = pDC->SelectObject (&m_penBack);
296            pDC->FillRect (rect,&m_brBackground);
297            pDC->SelectObject (pOldPen);
298           
299            // draw the up edges
300            pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
301            if ((HFONT)dispFont != NULL) dispFont.DeleteObject ();
302            dispFont.CreateFontIndirect (&lf); //Normal
303        }
304       
305        // draw the text if there is any
306        // We have to paint the text only if the image is nonexistant
307        dy = (rect.Height()-4-m_iconY)/2;
308        dy = dy<0 ? 0 : dy;
309       
310        if(checkflag||standardflag||selectedflag||disableflag){
311            rect2.SetRect(rect.left+1,rect.top+1+dy,rect.left+m_iconX+3,
312                rect.top+m_iconY+3+dy);
313            pDC->Draw3dRect (rect2,m_clrBack,m_clrBack);
314            if(checkflag && checkmaps){
315                pDC->FillRect (rect2,&m_brBackground);
316                rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
317                    rect.top+m_iconY+4+dy);
318               
319                pDC->Draw3dRect (rect2,m_clrBack,m_clrBack);
320                CPoint ptImage(rect.left+2,rect.top+2+dy);
321               
322                if(state&ODS_SELECTED)checkmaps->Draw(pDC,1,ptImage,ILD_TRANSPARENT);
323                else checkmaps->Draw(pDC,0,ptImage,ILD_TRANSPARENT);
324            }
325            else if(disableflag){
326                if(!selectedflag){
327                    HBITMAP hbmp=LoadSysColorBitmap(nIconNormal);
328                    if(hbmp){
329                        bitmapstandard.Attach(hbmp);
330                        rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
331                            rect.top+m_iconY+4+dy);
332                        pDC->Draw3dRect (rect2,m_clrBack,m_clrBack);
333                        if(disable_old_style)
334                            DitherBlt(lpDIS->hDC,rect.left+2,rect.top+2+dy,m_iconX,m_iconY,
335                            (HBITMAP)(bitmapstandard),xoffset*m_iconX,0);
336                        else
337                            DitherBlt2(pDC,rect.left+2,rect.top+2+dy,m_iconX,m_iconY,
338                            bitmapstandard,xoffset*m_iconX,0);
339                        bitmapstandard.DeleteObject();
340                    }
341                }
342            }
343            else if(selectedflag){
344                pDC->FillRect (rect2,&m_brBackground);
345                rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
346                    rect.top+m_iconY+4+dy);
347                if (IsNewShell()){
348                    if(state&ODS_CHECKED)
349                        pDC->Draw3dRect(rect2,GetSysColor(COLOR_3DSHADOW),
350                        GetSysColor(COLOR_3DHILIGHT));
351                    else
352                        pDC->Draw3dRect(rect2,GetSysColor(COLOR_3DHILIGHT),
353                        GetSysColor(COLOR_3DSHADOW));
354                }
355                CPoint ptImage(rect.left+2,rect.top+2+dy);
356                if(bitmap)bitmap->Draw(pDC,xoffset,ptImage,ILD_TRANSPARENT);
357            }
358            else{
359                if(state&ODS_CHECKED){
360                    CBrush brush;
361                    COLORREF col =GetSysColor(COLOR_3DLIGHT);
362                    brush.CreateSolidBrush(col);
363                    pDC->FillRect(rect2,&brush);
364                    brush.DeleteObject();
365                    rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
366                        rect.top+m_iconY+4+dy);
367                    if (IsNewShell())
368                        pDC->Draw3dRect(rect2,GetSysColor(COLOR_3DSHADOW),
369                        GetSysColor(COLOR_3DHILIGHT));
370                }
371                else{
372                    pDC->FillRect (rect2,&m_brBackground);
373                    rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
374                        rect.top+m_iconY+4+dy);
375                    pDC->Draw3dRect (rect2,m_clrBack,m_clrBack);
376                }
377                CPoint ptImage(rect.left+2,rect.top+2+dy);
378                if(bitmap)bitmap->Draw(pDC,xoffset,ptImage,ILD_TRANSPARENT);
379            }
380        }
381        if(nIconNormal<0 && state&ODS_CHECKED && !checkflag){
382            rect2.SetRect(rect.left+1,rect.top+2+dy,rect.left+m_iconX+1,
383                rect.top+m_iconY+2+dy);
384            CMenuItemInfo info;
385            info.fMask = MIIM_CHECKMARKS;
386      ::GetMenuItemInfo((HMENU)lpDIS->hwndItem,lpDIS->itemID,
387                MF_BYCOMMAND, &info);
388            if(state&ODS_CHECKED || info.hbmpUnchecked) {
389                Draw3DCheckmark(pDC, rect2, state&ODS_SELECTED,
390                    state&ODS_CHECKED ? info.hbmpChecked :
391                info.hbmpUnchecked);
392            }
393        }
394       
395        // This is needed always so that we can have the space for check marks
396        x0 = rect.left; y0 = rect.top;
397        rect.left = rect.left + m_iconX + 8 + GAP;
398       
399        if(!strText.IsEmpty())
400        {
401            CRect rectt(rect.left,rect.top-1,rect.right,rect.bottom-1);
402           
403            // Find tabs
404            CString leftStr,rightStr;
405            leftStr.Empty();rightStr.Empty();
406            int tablocr=strText.ReverseFind(_T('\t'));
407            if(tablocr!=-1)
408            {
409                rightStr=strText.Mid(tablocr+1);
410                leftStr=strText.Left(strText.Find(_T('\t')));
411                rectt.right-=m_iconX;
412            }
413            else leftStr=strText;
414           
415            int iOldMode = pDC->GetBkMode();
416            pDC->SetBkMode( TRANSPARENT);
417           
418            // Draw the text in the correct colour:
419            UINT nFormat  = DT_LEFT|DT_SINGLELINE|DT_VCENTER;
420            UINT nFormatr = DT_RIGHT|DT_SINGLELINE|DT_VCENTER;
421            if(!(lpDIS->itemState & ODS_GRAYED))
422            {
423                pDC->SetTextColor(crText);
424                pDC->DrawText (leftStr,rectt,nFormat);
425                if(tablocr!=-1) pDC->DrawText (rightStr,rectt,nFormatr);
426            }
427            else
428            {
429                // Draw the disabled text
430                if(!(state & ODS_SELECTED))
431                {
432                    RECT offset = *rectt;
433                    offset.left+=1;
434                    offset.right+=1;
435                    offset.top+=1;
436                    offset.bottom+=1;
437                    pDC->SetTextColor(GetSysColor(COLOR_BTNHILIGHT));
438                    pDC->DrawText(leftStr,&offset, nFormat);
439                    if(tablocr!=-1) pDC->DrawText (rightStr,&offset,nFormatr);
440                    pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
441                    pDC->DrawText(leftStr,rectt, nFormat);
442                    if(tablocr!=-1) pDC->DrawText (rightStr,rectt,nFormatr);
443                }
444                else
445                {
446                    // And the standard Grey text:
447                    pDC->SetTextColor(m_clrBack);
448                    pDC->DrawText(leftStr,rectt, nFormat);
449                    if(tablocr!=-1) pDC->DrawText (rightStr,rectt,nFormatr);
450                }
451            }
452            pFont = pDC->SelectObject (&dispFont);
453            pDC->SetBkMode( iOldMode );
454            pDC->SelectObject (pFont); //set it to the old font
455        }
456       
457        m_penBack.DeleteObject();
458        m_brBackground.DeleteObject();
459        m_fontMenu.DeleteObject();
460        m_brSelect.DeleteObject();
461        dispFont.DeleteObject ();
462    }
463}
464
465// Called by the framework when it wants to know what the width and height
466// of our item will be.  To accomplish this we provide the width of the
467// icon plus the width of the menu text, and then the height of the icon.
468void CBMPMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMIS)
469{
470    if (lpMIS->itemID >= ID_VIEWPORT01 && lpMIS->itemID <= ID_VIEWPORT14)
471    {
472        lpMIS->itemWidth = 41 + 8 - GetSystemMetrics(SM_CXMENUCHECK);
473        lpMIS->itemHeight = 33;
474        return;
475    }
476
477    UINT state = (((CBMPMenuData*)(lpMIS->itemData))->nFlags);
478    if(state & MF_SEPARATOR){
479        lpMIS->itemWidth = 0;
480        lpMIS->itemHeight = GetSystemMetrics(SM_CYMENU)>>1;
481    }
482    else{
483        CFont m_fontMenu;
484        LOGFONT m_lf;
485        ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
486        NONCLIENTMETRICS nm;
487        nm.cbSize = sizeof (NONCLIENTMETRICS);
488        VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
489            nm.cbSize,&nm,0));
490        m_lf =  nm.lfMenuFont;
491        m_fontMenu.CreateFontIndirect (&m_lf);
492       
493        // Obtain the width of the text:
494        CWnd *pWnd = AfxGetMainWnd();            // Get main window
495        CDC *pDC = pWnd->GetDC();              // Get device context
496        CFont* pFont;    // Select menu font in...
497       
498        if (IsNewShell())
499            pFont = pDC->SelectObject (&m_fontMenu);// Select menu font in...
500       
501        //Get pointer to text SK
502        const wchar_t *lpstrText = ((CBMPMenuData*)(lpMIS->itemData))->GetWideString();//SK: we use const to prevent misuse
503       
504       
505        SIZE size;
506       
507        if (Win32s!=g_Shell)
508            VERIFY(::GetTextExtentPoint32W(pDC->m_hDC,lpstrText,
509            wcslen(lpstrText),&size)); //SK should also work on 95
510#ifndef UNICODE //can't be UNICODE for Win32s
511        else{//it's Win32suckx
512            RECT rect;
513            rect.left=rect.top=0;
514            size.cy=DrawText(pDC->m_hDC,(LPCTSTR)lpstrText,
515                wcslen(lpstrText),&rect,
516                DT_SINGLELINE|DT_LEFT|DT_VCENTER|DT_CALCRECT);
517            //+3 makes at least three pixels space to the menu border
518            size.cx=rect.right-rect.left+3;
519            size.cx += 3*(size.cx/wcslen(lpstrText));
520        }
521#endif   
522       
523        CSize t = CSize(size);
524        if(IsNewShell())
525            pDC->SelectObject (pFont);  // Select old font in
526        AfxGetMainWnd()->ReleaseDC(pDC);  // Release the DC
527       
528        // Set width and height:
529       
530        lpMIS->itemWidth = m_iconX + t.cx + m_iconX + GAP;
531        int temp = GetSystemMetrics(SM_CYMENU);
532        lpMIS->itemHeight = temp>m_iconY+4 ? temp : m_iconY+4;
533        m_fontMenu.DeleteObject();
534    }
535}
536
537void CBMPMenu::SetIconSize (int width, int height)
538{
539    m_iconX = width;
540    m_iconY = height;
541}
542
543BOOL CBMPMenu::AppendODMenuA(LPCSTR lpstrText,UINT nFlags,UINT nID, int nIconNormal)
544{
545    ASSERT(lpstrText);
546    USES_CONVERSION;
547    return AppendODMenuW(A2W(lpstrText),nFlags,nID,nIconNormal);//SK: See MFC Tech Note 059
548}
549
550BOOL CBMPMenu::AppendODMenuW(wchar_t *lpstrText,UINT nFlags,UINT nID, int nIconNormal)
551{
552    // Add the MF_OWNERDRAW flag if not specified:
553    ASSERT(lpstrText);
554    if(!nID)nFlags=MF_SEPARATOR|MF_OWNERDRAW;
555    else if(!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
556   
557    CBMPMenuData *mdata = new CBMPMenuData;
558    m_MenuList.Add(mdata);
559    mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
560   
561    mdata->menuIconNormal = nIconNormal;
562    mdata->xoffset=-1;
563    if(nIconNormal>=0){
564        mdata->xoffset=0;
565        LoadFromToolBar(nID,nIconNormal,mdata->xoffset);
566        if(mdata->bitmap)mdata->bitmap->DeleteImageList();
567        else mdata->bitmap=new(CImageList);
568        mdata->bitmap->Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
569        AddBitmapToImageList(mdata->bitmap,nIconNormal);
570    }
571    mdata->nFlags = nFlags;
572    mdata->nID = nID;
573    return(CMenu::AppendMenu(nFlags, nID, (LPCTSTR)mdata));
574}
575
576BOOL CBMPMenu::ModifyODMenuA(const char * lpstrText,UINT nID,int nIconNormal)
577{
578    USES_CONVERSION;
579    return ModifyODMenuW(A2W(lpstrText),nID,nIconNormal);//SK: see MFC Tech Note 059
580}
581
582BOOL CBMPMenu::ModifyODMenuW(wchar_t *lpstrText,UINT nID,int nIconNormal)
583{
584    int nLoc;
585    CBMPMenuData *mdata;
586   
587    // Find the old CBMPMenuData structure:
588    CBMPMenu *psubmenu = FindMenuOption(nID,nLoc);
589    if(psubmenu && nLoc>=0)mdata = psubmenu->m_MenuList[nLoc];
590    else{
591        // Create a new CBMPMenuData structure:
592        mdata = new CBMPMenuData;
593        m_MenuList.Add(mdata);
594    }
595    ASSERT(mdata);
596    if(lpstrText)
597        mdata->SetWideString(lpstrText);  //SK: modified for dynamic allocation
598    mdata->menuIconNormal = nIconNormal;
599    mdata->xoffset=-1;
600    if(nIconNormal>=0){
601        mdata->xoffset=0;
602        LoadFromToolBar(nID,nIconNormal,mdata->xoffset);
603        if(mdata->bitmap)mdata->bitmap->DeleteImageList();
604        else mdata->bitmap=new(CImageList);
605        mdata->bitmap->Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
606        AddBitmapToImageList(mdata->bitmap,nIconNormal);
607    }
608    mdata->nFlags = MF_BYCOMMAND | MF_OWNERDRAW;
609    mdata->nID = nID;
610    return (CMenu::ModifyMenu(nID,mdata->nFlags,nID,(LPCTSTR)mdata));
611}
612
613BOOL CBMPMenu::ModifyODMenuA(const char *lpstrText,const char *OptionText, int nIconNormal)
614{
615    USES_CONVERSION;
616    return ModifyODMenuW(A2W(lpstrText),A2W(OptionText),nIconNormal);//SK: see MFC  Tech Note 059
617}
618
619
620BOOL CBMPMenu::ModifyODMenuW(wchar_t *lpstrText,wchar_t *OptionText, int nIconNormal)
621{
622    CBMPMenuData *mdata;
623   
624    // Find the old CBMPMenuData structure:
625    mdata=FindMenuOption(OptionText);
626    if(mdata){
627        if(lpstrText)
628            mdata->SetWideString(lpstrText);//SK: modified for dynamic allocation
629        mdata->menuIconNormal = nIconNormal;
630        mdata->xoffset=-1;
631        if(nIconNormal>=0)
632        {
633            mdata->xoffset=0;
634            if(mdata->bitmap)mdata->bitmap->DeleteImageList();
635            else mdata->bitmap=new(CImageList);
636            mdata->bitmap->Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
637            AddBitmapToImageList(mdata->bitmap,nIconNormal);
638        }
639        return(TRUE);
640    }
641    return(FALSE);
642}
643
644CBMPMenuData *CBMPMenu::NewODMenu(UINT pos,UINT nFlags,UINT nID,CString string)
645{
646    CBMPMenuData *mdata;
647   
648    mdata = new CBMPMenuData;
649    mdata->menuIconNormal = -1;
650    mdata->xoffset=-1;
651#ifdef UNICODE
652    mdata->SetWideString((LPCTSTR)string);//SK: modified for dynamic allocation
653#else
654    mdata->SetAnsiString(string);
655#endif
656    mdata->nFlags = nFlags;
657    mdata->nID = nID;
658   
659   
660    if (nFlags & MF_OWNERDRAW)
661    {
662        ASSERT(!(nFlags&MF_STRING));
663        ModifyMenu(pos,nFlags,nID,(LPCTSTR)mdata);
664    }
665    else if (nFlags & MF_STRING)
666    {
667        ASSERT(!(nFlags&MF_OWNERDRAW));
668        ModifyMenu(pos,nFlags,nID,mdata->GetString());
669    }
670    else{
671        ASSERT(nFlags & MF_SEPARATOR);
672        ModifyMenu(pos,nFlags,nID);
673    }
674   
675    return(mdata);
676};
677
678BOOL CBMPMenu::LoadToolbars(const UINT *arID,int n)
679{
680    ASSERT(arID);
681    BOOL bRet=TRUE;
682    for(int i=0;i<n;++i)
683        bRet |= LoadToolbar(arID[i]);
684    return(bRet);
685}
686
687BOOL CBMPMenu::LoadToolbar(UINT nToolBar)
688{
689    UINT nID;
690    BOOL returnflag=FALSE;
691    CToolBar bar;
692   
693    bar.Create(AfxGetMainWnd());
694    if(bar.LoadToolBar(nToolBar)){
695        for(int i=0;i<bar.GetCount();++i){
696            nID = bar.GetItemID(i);
697            if(nID && GetMenuState(nID, MF_BYCOMMAND)
698                !=0xFFFFFFFF)ModifyODMenu(NULL,nID,nToolBar);
699        }
700        returnflag=TRUE;
701    }
702    return(returnflag);
703}
704
705BOOL CBMPMenu::LoadFromToolBar(UINT nID,UINT nToolBar,int& xoffset)
706{
707    int xset,offset;
708    UINT nStyle;
709    BOOL returnflag=FALSE;
710    CToolBar bar;
711   
712    CWnd* pWnd = AfxGetMainWnd();
713    if (pWnd == NULL)pWnd = CWnd::GetDesktopWindow();
714    bar.Create(pWnd);
715    if(bar.LoadToolBar(nToolBar)){
716        offset=bar.CommandToIndex(nID);
717        if(offset>=0){
718            bar.GetButtonInfo(offset,nID,nStyle,xset);
719            if(xset>0)xoffset=xset;
720            returnflag=TRUE;
721        }
722    }
723    return(returnflag);
724}
725
726CBMPMenu *CBMPMenu::FindMenuOption(int nId,int& nLoc)
727{
728    int i;
729    CBMPMenu *psubmenu,*pgoodmenu;
730   
731    for(i=0;i<(int)(GetMenuItemCount());++i){
732#ifdef _CPPRTTI 
733        psubmenu=dynamic_cast<CBMPMenu *>(GetSubMenu(i));
734#else
735        psubmenu=(CBMPMenu *)GetSubMenu(i);
736#endif
737        if(psubmenu){
738            pgoodmenu=psubmenu->FindMenuOption(nId,nLoc);
739            if(pgoodmenu)return(pgoodmenu);
740        }
741        else if(nId==(int)GetMenuItemID(i)){
742            nLoc=i;
743            return(this);
744        }
745    }
746    nLoc = -1;
747    return(NULL);
748}
749
750CBMPMenuData *CBMPMenu::FindMenuOption(wchar_t *lpstrText)
751{
752    int i;
753    CBMPMenu *psubmenu;
754    CBMPMenuData *pmenulist;
755   
756    for(i=0;i<(int)(GetMenuItemCount());++i){
757#ifdef _CPPRTTI 
758        psubmenu=dynamic_cast<CBMPMenu *>(GetSubMenu(i));
759#else
760        psubmenu=(CBMPMenu *)GetSubMenu(i);
761#endif
762        if(psubmenu){
763            pmenulist=psubmenu->FindMenuOption(lpstrText);
764            if(pmenulist)return(pmenulist);
765        }
766        else{
767            const wchar_t *szWide;//SK: we use const to prevent misuse of this Ptr
768            for(i=0;i<=m_MenuList.GetUpperBound();++i){     
769                szWide = m_MenuList[i]->GetWideString ();
770                if(szWide && !wcscmp(lpstrText,szWide))//SK: modified for dynamic allocation
771                    return(m_MenuList[i]);
772            }
773        }
774    }
775    return(NULL);
776}
777
778
779BOOL CBMPMenu::LoadMenu(int nResource)
780{
781    return(CBMPMenu::LoadMenu(MAKEINTRESOURCE(nResource)));
782}
783
784BOOL CBMPMenu::LoadMenu(LPCTSTR lpszResourceName)
785{
786    TRACE(_T(
787        "IMPORTANT:Use CBMPMenu::DestroyMenu to destroy Loaded Menu's\n"));
788    ASSERT_VALID(this);
789    ASSERT(lpszResourceName != NULL);
790   
791    // Find the Menu Resource:
792    HINSTANCE m_hInst = AfxFindResourceHandle(lpszResourceName,RT_MENU);
793    HRSRC hRsrc = ::FindResource(m_hInst,lpszResourceName,RT_MENU);
794    if(hRsrc == NULL)return FALSE;
795   
796    // Get size of resource:
797   
798    DWORD dwSize = SizeofResource(NULL, hRsrc);
799   
800    // Load the Menu Resource:
801   
802    HGLOBAL hGlobal = LoadResource(m_hInst, hRsrc);
803    if(hGlobal == NULL)return FALSE;
804   
805    // Attempt to create us as a menu...
806   
807    if(!CMenu::CreateMenu())return FALSE;
808   
809    // Get Item template Header, and calculate offset of MENUITEMTEMPLATES
810   
811    MENUITEMTEMPLATEHEADER *pTpHdr=
812        (MENUITEMTEMPLATEHEADER*)LockResource(hGlobal);
813    BYTE* pTp=(BYTE*)pTpHdr +
814        (sizeof(MENUITEMTEMPLATEHEADER) + pTpHdr->offset);
815   
816   
817    // Variables needed during processing of Menu Item Templates:
818   
819    int j=0;
820    CBMPMenuData*  pData = NULL;              // New OD Menu Item Data
821    WORD    dwFlags = 0;              // Flags of the Menu Item
822    WORD    dwID  = 0;              // ID of the Menu Item
823    UINT    uFlags;                  // Actual Flags.
824    wchar_t *szCaption=NULL;
825    int      nLen   = 0;                // Length of caption
826    CTypedPtrArray<CPtrArray, CBMPMenu*>  m_Stack;    // Popup menu stack
827    CArray<BOOL,BOOL>  m_StackEnd;    // Popup menu stack
828    m_Stack.Add(this);                  // Add it to this...
829    m_StackEnd.Add(FALSE);
830   
831    do{
832        // Obtain Flags and (if necessary), the ID...
833        memcpy(&dwFlags, pTp, sizeof(WORD));pTp+=sizeof(WORD);// Obtain Flags
834        if(!(dwFlags & MF_POPUP)){
835            memcpy(&dwID, pTp, sizeof(WORD)); // Obtain ID
836            pTp+=sizeof(WORD);
837        }
838        else dwID = 0;
839       
840        uFlags = (UINT)dwFlags; // Remove MF_END from the flags that will
841        if(uFlags & MF_END) // be passed to the Append(OD)Menu functions.
842            uFlags -= MF_END;
843       
844        // Obtain Caption (and length)
845       
846        nLen = 0;
847        char *ch = (char*)pTp;
848        szCaption=new wchar_t[wcslen((wchar_t *)pTp)+1];
849        wcscpy(szCaption,(wchar_t *)pTp);
850        pTp=&pTp[(wcslen((wchar_t *)pTp)+1)*sizeof(wchar_t)];//modified SK
851       
852        // Handle popup menus first....
853       
854        //WideCharToMultiByte
855        if(dwFlags & MF_POPUP){
856            if(dwFlags & MF_END)m_StackEnd.SetAt(m_Stack.GetUpperBound(),TRUE);
857            CBMPMenu* pSubMenu = new CBMPMenu;
858            pSubMenu->m_unselectcheck=m_unselectcheck;
859            pSubMenu->m_selectcheck=m_selectcheck;
860            pSubMenu->checkmaps=checkmaps;
861            pSubMenu->checkmapsshare=TRUE;
862            pSubMenu->CreatePopupMenu();
863           
864            // Append it to the top of the stack:
865           
866            m_Stack[m_Stack.GetUpperBound()]->AppendODMenuW(szCaption,uFlags,
867                (UINT)pSubMenu->m_hMenu, -1);
868            m_Stack.Add(pSubMenu);
869            m_StackEnd.Add(FALSE);
870            m_SubMenus.Add(pSubMenu);
871        }
872        else {
873            m_Stack[m_Stack.GetUpperBound()]->AppendODMenuW(szCaption, uFlags,
874                dwID, -1);
875            if(dwFlags & MF_END)m_StackEnd.SetAt(m_Stack.GetUpperBound(),TRUE);
876            j = m_Stack.GetUpperBound();
877            while(j>=0 && m_StackEnd.GetAt(j)){
878                m_Stack[m_Stack.GetUpperBound()]->InsertSpaces();
879                m_Stack.RemoveAt(j);
880                m_StackEnd.RemoveAt(j);
881                --j;
882            }
883        }
884       
885        delete[] szCaption;
886    }while(m_Stack.GetUpperBound() != -1);
887   
888    for(int i=0;i<(int)GetMenuItemCount();++i){
889        CString str=m_MenuList[i]->GetString();
890       
891        if(GetSubMenu(i)){
892            m_MenuList[i]->nFlags=MF_POPUP|MF_BYPOSITION;
893            ModifyMenu(i,MF_POPUP|MF_BYPOSITION,
894                (UINT)GetSubMenu(i)->m_hMenu,str);
895        }
896        else{
897            m_MenuList[i]->nFlags=MF_STRING|MF_BYPOSITION;
898            ModifyMenu(i,MF_STRING|MF_BYPOSITION,m_MenuList[i]->nID,str);
899        }
900    }
901   
902    return(TRUE);
903}
904
905void CBMPMenu::InsertSpaces(void)
906{
907    int i,j,numitems,maxlength;
908    CString string,newstring;
909    CSize t;
910    CFont m_fontMenu;
911    LOGFONT m_lf;
912   
913    ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
914    NONCLIENTMETRICS nm;
915    nm.cbSize = sizeof (NONCLIENTMETRICS);
916    VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,nm.cbSize,&nm,0));
917    m_lf =  nm.lfMenuFont;
918    m_fontMenu.CreateFontIndirect (&m_lf);
919   
920    CWnd *pWnd = AfxGetMainWnd(); 
921    if (pWnd == NULL)pWnd = CWnd::GetDesktopWindow();
922    CDC *pDC = pWnd->GetDC();
923    CFont* pFont = pDC->SelectObject (&m_fontMenu);
924   
925    numitems=GetMenuItemCount();
926    maxlength = -1;
927    for(i=0;i<numitems;++i){
928        string=m_MenuList[i]->GetString();
929        j=string.Find((char)9);
930        newstring.Empty();
931        if(j!=-1)newstring.Left(j);
932        else newstring=string;
933        newstring+=_T(" ");//SK: modified for Unicode correctness.
934        LPCTSTR lpstrText = (LPCTSTR)newstring;
935        t=pDC->GetTextExtent(lpstrText,_tcslen(lpstrText));
936        if(t.cx>maxlength)maxlength = t.cx;
937    }
938    for(i=0;i<numitems;++i){
939        string=m_MenuList[i]->GetString();
940       
941        j=string.Find((char)9);
942        if(j!=-1){
943            newstring.Empty();
944            newstring=string.Left(j);
945            LPCTSTR lpstrText = (LPCTSTR)(newstring);
946            t=pDC->GetTextExtent(lpstrText,_tcslen(lpstrText));
947            while(t.cx<maxlength){
948                newstring+=_T(' ');//SK: modified for Unicode correctness
949                LPCTSTR lpstrText = (LPCTSTR)(newstring);
950                t=pDC->GetTextExtent(lpstrText,_tcslen(lpstrText));
951            }
952            newstring+=string.Mid(j);
953#ifdef UNICODE     
954            m_MenuList[i]->SetWideString(newstring);//SK: modified for dynamic allocation
955#else
956            m_MenuList[i]->SetAnsiString(newstring);
957#endif
958        }
959    }
960    pDC->SelectObject (pFont);              // Select old font in
961    pWnd->ReleaseDC(pDC);       // Release the DC
962    m_fontMenu.DeleteObject();
963}
964
965void CBMPMenu::LoadCheckmarkBitmap(int unselect, int select)
966{
967    if(unselect>=0 && select>=0){
968        m_selectcheck=select;
969        m_unselectcheck=unselect;
970        if(checkmaps)checkmaps->DeleteImageList();
971        else checkmaps=new(CImageList);
972        checkmaps->Create(m_iconX,m_iconY,TRUE,2,1);
973        AddBitmapToImageList(checkmaps,unselect);
974        AddBitmapToImageList(checkmaps,select);
975    }
976}
977
978BOOL CBMPMenu::GetMenuText(UINT id, CString& string)
979{
980    BOOL returnflag=FALSE;
981   
982    UINT numMenuItems = m_MenuList.GetUpperBound();
983    if(id<=numMenuItems){
984        string=m_MenuList[id]->GetString();
985        returnflag=TRUE;
986    }
987    return(returnflag);
988}
989
990void CBMPMenu::DrawRadioDot(CDC *pDC,int x,int y,COLORREF color)
991{
992    CRect rcDot(x,y,x+6,y+6);
993    CBrush brush;
994    CPen pen;
995    brush.CreateSolidBrush(color);
996    pen.CreatePen(PS_SOLID,0,color);
997    pDC->SelectObject(&brush);
998    pDC->SelectObject(&pen);
999    pDC->Ellipse(&rcDot);
1000    pen.DeleteObject();
1001    brush.DeleteObject();
1002}
1003
1004void CBMPMenu::DrawCheckMark(CDC* pDC,int x,int y,COLORREF color)
1005{
1006    pDC->SetPixel(x,y+2,color);
1007    pDC->SetPixel(x,y+3,color);
1008    pDC->SetPixel(x,y+4,color);
1009   
1010    pDC->SetPixel(x+1,y+3,color);
1011    pDC->SetPixel(x+1,y+4,color);
1012    pDC->SetPixel(x+1,y+5,color);
1013   
1014    pDC->SetPixel(x+2,y+4,color);
1015    pDC->SetPixel(x+2,y+5,color);
1016    pDC->SetPixel(x+2,y+6,color);
1017   
1018    pDC->SetPixel(x+3,y+3,color);
1019    pDC->SetPixel(x+3,y+4,color);
1020    pDC->SetPixel(x+3,y+5,color);
1021   
1022    pDC->SetPixel(x+4,y+2,color);
1023    pDC->SetPixel(x+4,y+3,color);
1024    pDC->SetPixel(x+4,y+4,color);
1025   
1026    pDC->SetPixel(x+5,y+1,color);
1027    pDC->SetPixel(x+5,y+2,color);
1028    pDC->SetPixel(x+5,y+3,color);
1029   
1030    pDC->SetPixel(x+6,y,color);
1031    pDC->SetPixel(x+6,y+1,color);
1032    pDC->SetPixel(x+6,y+2,color);
1033}
1034
1035CBMPMenuData *CBMPMenu::FindMenuList(UINT nID)
1036{
1037    for(int i=0;i<=m_MenuList.GetUpperBound();++i){
1038        if(m_MenuList[i]->nID==nID && !m_MenuList[i]->syncflag){
1039            m_MenuList[i]->syncflag=1;
1040            return(m_MenuList[i]);
1041        }
1042    }
1043    return(NULL);
1044}
1045
1046void CBMPMenu::InitializeMenuList(int value)
1047{
1048    for(int i=0;i<=m_MenuList.GetUpperBound();++i)
1049        m_MenuList[i]->syncflag=value;
1050}
1051
1052void CBMPMenu::DeleteMenuList(void)
1053{
1054    for(int i = 0;i <= m_MenuList.GetUpperBound(); ++i)
1055        if(!m_MenuList[i]->sy