1 // ODMenu.cpp
2 //
3 
4 #include "odmenu.h"
5 
6 using namespace std;
7 
ODMenu()8 ODMenu::ODMenu()
9 {
10     m_seqNumber = 0;
11 
12     //Set default colors
13 
14     //Transparent color is color of "transparent" background in bitmaps
15     m_clrTranparent = RGB(192, 192, 192);
16 
17     m_clrItemText = GetSysColor(COLOR_MENUTEXT);
18     m_clrItemBackground = GetSysColor(COLOR_MENU);
19     if(GetColorIntensity(m_clrItemBackground) < 0.82)
20         m_clrItemBackground = LightenColor(m_clrItemBackground, 0.27);
21     else
22         m_clrItemBackground = DarkenColor(m_clrItemBackground, 0.10);
23     m_clrHighlightItemText = GetSysColor(COLOR_HIGHLIGHTTEXT);
24     m_clrHighlightItemBackground = GetSysColor(COLOR_HIGHLIGHT);
25     m_clrHighlightItemBackground = LightenColor(m_clrHighlightItemBackground, 0.5);
26     m_clrHighlightItemOutline = GetSysColor(COLOR_HIGHLIGHT);
27     m_clrSeparator = GetSysColor(COLOR_3DSHADOW);
28     m_clrIconBar = GetSysColor(COLOR_MENU);
29     m_clrIconShadow = GetSysColor(COLOR_3DSHADOW);
30     m_clrCheckMark = GetSysColor(COLOR_MENUTEXT);
31     m_clrCheckMarkBackground = AverageColor(m_clrIconBar, m_clrHighlightItemBackground, 0.8);
32     m_clrCheckMarkBackgroundHighlight = AverageColor(m_clrIconBar, m_clrHighlightItemBackground, 0.25);
33     m_clrCheckMarkBackgroundHighlight = DarkenColor(m_clrHighlightItemBackground, 0.1);
34 
35     //Get the system font for menus
36     NONCLIENTMETRICS ncms;
37     ncms.cbSize = sizeof(NONCLIENTMETRICS);
38     if(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncms, 0))
39         m_hFont = CreateFontIndirect(&ncms.lfMenuFont);
40 
41     //Set menu metrics
42     m_iconBarMargin = 3;
43     m_textLeftMargin = 6;
44     m_textRightMargin = 3;
45     m_iconWidth = GetSystemMetrics(SM_CXSMICON);
46     m_iconHeight = GetSystemMetrics(SM_CYSMICON);
47     m_verticalSpacing = 6;
48 
49     //Create GDI objects
50     m_hItemBackground = CreateSolidBrush(m_clrItemBackground);
51     m_hIconBarBrush = CreateSolidBrush(m_clrIconBar);
52     m_hIconShadowBrush = CreateSolidBrush(m_clrIconShadow);
53     m_hHighlightItemBackgroundBrush = CreateSolidBrush(m_clrHighlightItemBackground);
54     m_hCheckMarkBackgroundBrush = CreateSolidBrush(m_clrCheckMarkBackground);
55     m_hCheckMarkBackgroundHighlightBrush = CreateSolidBrush(m_clrCheckMarkBackgroundHighlight);
56     m_hSelectionOutlinePen = CreatePen(PS_SOLID, 1, m_clrHighlightItemOutline);
57     m_hSeparatorPen = CreatePen(PS_SOLID, 1, m_clrSeparator);
58     m_hCheckMarkPen = CreatePen(PS_SOLID, 1, m_clrCheckMark);
59 }
60 
~ODMenu()61 ODMenu::~ODMenu()
62 {
63     if(m_hFont)
64         DeleteObject(m_hFont);
65     if(m_hIconBarBrush)
66         DeleteObject(m_hIconBarBrush);
67     if(m_hIconShadowBrush)
68         DeleteObject(m_hIconShadowBrush);
69     if(m_hCheckMarkBackgroundBrush)
70         DeleteObject(m_hCheckMarkBackgroundBrush);
71     if(m_hCheckMarkBackgroundHighlightBrush)
72         DeleteObject(m_hCheckMarkBackgroundHighlightBrush);
73     if(m_hSelectionOutlinePen)
74         DeleteObject(m_hSelectionOutlinePen);
75     if(m_hSeparatorPen)
76         DeleteObject(m_hSeparatorPen);
77     if(m_hCheckMarkPen)
78         DeleteObject(m_hCheckMarkPen);
79     if(m_hItemBackground)
80         DeleteObject(m_hItemBackground);
81     if(m_hHighlightItemBackgroundBrush)
82         DeleteObject(m_hHighlightItemBackgroundBrush);
83 }
84 
Init(HWND hOwnerWnd,HMENU hMenu)85 bool ODMenu::Init(HWND hOwnerWnd, HMENU hMenu)
86 {
87     m_hRootMenu = hMenu;
88 
89     //Traverse through all menu items to allocate a map of ODMENUITEM which
90     //will be subsequently used to measure and draw menu items.
91 
92     if(m_seqNumber == 0)
93         EnumMenuItems(hMenu);
94 
95     return true;
96 }
97 
EnumMenuItems(HMENU hMenu)98 void ODMenu::EnumMenuItems(HMENU hMenu)
99 {
100     int i, numItems;
101     MENUITEMINFO miInfo;
102 
103     numItems = GetMenuItemCount(hMenu);
104     if(numItems > 0)
105     {
106         miInfo.cbSize = sizeof(MENUITEMINFO);
107         miInfo.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_ID;
108         miInfo.dwTypeData = m_szItemText;
109         miInfo.cch = sizeof(m_szItemText);
110 
111         for(i=0; i<numItems; i++)
112         {
113             if(GetMenuItemInfo(hMenu, i, TRUE, &miInfo))
114                 AddItem(hMenu, i, &miInfo);
115 
116             if(miInfo.hSubMenu)
117             {
118                 //Resursive call
119                 EnumMenuItems(miInfo.hSubMenu);
120             }
121 
122             miInfo.cch = sizeof(m_szItemText);
123             miInfo.dwTypeData = m_szItemText;
124         }
125     }
126 }
127 
DeleteSubMenu(HMENU hMenu)128 void ODMenu::DeleteSubMenu(HMENU hMenu)
129 {
130     //Recursively remove map items according to menu structure
131     int i;
132     MENUITEMINFO miInfo;
133 
134     i = 0;
135     miInfo.cbSize = sizeof(MENUITEMINFO);
136     miInfo.fMask = MIIM_SUBMENU | MIIM_DATA;
137     while(GetMenuItemInfo(hMenu, i, TRUE, &miInfo))
138     {
139         //Make recursive call
140         if(miInfo.hSubMenu)
141             DeleteSubMenu(miInfo.hSubMenu);
142 
143         //Remove this item from map
144         m_menuItems.erase(miInfo.dwItemData);
145 
146         i++;
147     }
148 }
149 
SetMenuItemOwnerDrawn(HMENU hMenu,UINT item,UINT type)150 void ODMenu::SetMenuItemOwnerDrawn(HMENU hMenu, UINT item, UINT type)
151 {
152     //Set menu item type to owner-drawn and set itemdata to sequence number.
153     MENUITEMINFO miInfo;
154 
155     miInfo.cbSize = sizeof(MENUITEMINFO);
156     miInfo.fMask = MIIM_TYPE | MIIM_DATA;
157     miInfo.fType = type | MFT_OWNERDRAW;
158     miInfo.dwItemData = m_seqNumber++;
159 
160     SetMenuItemInfo(hMenu, item, TRUE, &miInfo);
161 }
162 
GenerateDisplayText(ODMENUITEM & item)163 void ODMenu::GenerateDisplayText(ODMENUITEM& item)
164 {
165     TCHAR* pChr;
166     int i;
167 
168     item.displayText = "";
169     item.rawDisplayText = "";
170     item.shortcutText = "";
171 
172     //Does shortcut text exist?
173     if(pChr = strchr((LPTSTR)item.rawText.c_str(), '\t'))
174         item.shortcutText = pChr + 1;
175 
176     i = 0;
177     pChr = (LPTSTR)item.rawText.c_str();
178     while(*(pChr + i) != '\t' && *(pChr + i) != '\0')
179     {
180         if(*(pChr + i) == '&')
181         {
182             item.rawDisplayText.append(pChr + i, 1);
183             i++;
184 
185             continue;
186         }
187 
188         item.rawDisplayText.append(pChr + i, 1);
189         item.displayText.append(pChr + i, 1);
190         i++;
191     }
192 }
193 
DrawItemText(DRAWITEMSTRUCT * lpdis,ODMENUITEM & item)194 void ODMenu::DrawItemText(DRAWITEMSTRUCT* lpdis, ODMENUITEM& item)
195 {
196     int x, y;
197     SIZE size;
198     RECT rectText;
199     RECT rectItem;
200 
201     memcpy(&rectItem, &lpdis->rcItem, sizeof(RECT));
202 
203     //Get size of text to draw
204     GetTextExtentPoint32(lpdis->hDC, item.displayText.c_str(), item.displayText.length(), &size);
205 
206     // Determine where to draw.
207     ComputeMenuTextPos(lpdis, item, x, y, size);
208 
209     rectText.left = x;
210     rectText.right = lpdis->rcItem.right - m_textRightMargin;
211     rectText.top = y;
212     rectText.bottom = lpdis->rcItem.bottom;
213 
214     //Adjust rectangle that will contain the menu item
215     if(!item.topMost)
216     {
217         rectItem.left += (m_iconWidth + 2*m_iconBarMargin);
218     }
219 
220     //Draw the item rectangle with appropriate background color
221     ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE, &rectItem, "", 0, NULL);
222 
223     //Draw the text
224     DrawText(lpdis->hDC, item.rawDisplayText.c_str(), item.rawDisplayText.length(),
225         &rectText, DT_LEFT | DT_SINGLELINE | DT_VCENTER);
226     DrawText(lpdis->hDC, item.shortcutText.c_str(), item.shortcutText.length(),
227         &rectText, DT_RIGHT | DT_SINGLELINE | DT_VCENTER);
228 }
229 
DrawIconBar(DRAWITEMSTRUCT * lpdis,ODMENUITEM & item)230 void ODMenu::DrawIconBar(DRAWITEMSTRUCT* lpdis, ODMENUITEM& item)
231 {
232     RECT rectBar;
233     memcpy(&rectBar, &lpdis->rcItem, sizeof(RECT));
234 
235     //Draw icon bar if not top level
236     if(!item.topMost)
237     {
238         rectBar.right = rectBar.left + m_iconWidth + 2*m_iconBarMargin + 1;
239         if(lpdis->itemState & ODS_SELECTED &&
240                 !(lpdis->itemState & ODS_DISABLED || lpdis->itemState & ODS_GRAYED))
241         {
242             FillRect(lpdis->hDC, &rectBar, m_hHighlightItemBackgroundBrush);
243         }
244         else
245         {
246             FillRect(lpdis->hDC, &rectBar, m_hIconBarBrush);
247         }
248 
249     }
250 
251     int x, y;
252 
253     //Draw icon for menu item if handle is valid
254     if(item.hBitmap)
255     {
256         x = m_iconBarMargin;
257         y = rectBar.top + ((rectBar.bottom - rectBar.top - 16) / 2);
258 
259         if(lpdis->itemState & ODS_DISABLED || lpdis->itemState & ODS_GRAYED)
260         {
261             //Draw disabled icon in normal position
262             DrawTransparentBitmap(lpdis->hDC, item.hBitmap, x, y, m_clrTranparent, eDisabled);
263         }
264         else if(lpdis->itemState & ODS_SELECTED)
265         {
266             //Draw icon "raised"
267             //Draw shadow right one pixel and down one pixel from normal position
268             DrawTransparentBitmap(lpdis->hDC, item.hBitmap, x+1, y+1, m_clrTranparent, eShadow);
269 
270             //Draw normal left one pixel and up one pixel from normal position
271             DrawTransparentBitmap(lpdis->hDC, item.hBitmap, x-1, y-1, m_clrTranparent);
272         }
273         else
274         {
275             //Draw faded icon in normal position
276             DrawTransparentBitmap(lpdis->hDC, item.hBitmap, x, y, m_clrTranparent, eFaded);
277         }
278     }
279     else if(lpdis->itemState & ODS_CHECKED)
280     {
281         HBRUSH hPrevBrush;
282         HPEN hPrevPen;
283         RECT rect;
284 
285         //Draw filled, outlined rectangle around checkmark first
286         if(lpdis->itemState & ODS_SELECTED)
287             hPrevBrush = (HBRUSH)SelectObject(lpdis->hDC, m_hCheckMarkBackgroundHighlightBrush);
288         else
289             hPrevBrush = (HBRUSH)SelectObject(lpdis->hDC, m_hCheckMarkBackgroundBrush);
290         hPrevPen = (HPEN)SelectObject(lpdis->hDC, m_hSelectionOutlinePen);
291         rect.left = m_iconBarMargin;
292         rect.right = m_iconBarMargin + m_iconWidth;
293         rect.top = rectBar.top + (rectBar.bottom - rectBar.top - m_iconHeight) / 2;
294         rect.bottom = rect.top + m_iconHeight;
295         Rectangle(lpdis->hDC, rect.left, rect.top, rect.right, rect.bottom);
296         SelectObject(lpdis->hDC, hPrevBrush);
297         SelectObject(lpdis->hDC, hPrevPen);
298 
299         //Draw check mark
300         x = (m_iconWidth + 2*m_iconBarMargin - 6) / 2;
301         y = rectBar.top + ((rectBar.bottom - rectBar.top - 7) / 2) + 1;
302         DrawCheckMark(lpdis->hDC, x, y, true);
303     }
304 }
305 
ComputeMenuTextPos(DRAWITEMSTRUCT * lpdis,ODMENUITEM & item,int & x,int & y,SIZE & size)306 void ODMenu::ComputeMenuTextPos(DRAWITEMSTRUCT* lpdis, ODMENUITEM& item, int& x, int& y, SIZE& size)
307 {
308     x = lpdis->rcItem.left;
309     y = lpdis->rcItem.top;
310 //    y += ((lpdis->rcItem.bottom - lpdis->rcItem.top - size.cy) / 2);
311 
312     if(!item.topMost)
313     {
314         //Correct position for drop down menus. Leave space for a bitmap
315         x += (m_iconWidth + 2*m_iconBarMargin + m_textLeftMargin);
316     }
317     else
318     {
319         //Center horizontally for top level menu items
320         x += ((lpdis->rcItem.right - lpdis->rcItem.left - size.cx) / 2);
321     }
322 }
323 
DrawTransparentBitmap(HDC hDC,HBITMAP hBitmap,short xStart,short yStart,COLORREF cTransparentColor,bitmapType eType)324 void ODMenu::DrawTransparentBitmap(HDC hDC, HBITMAP hBitmap, short xStart,
325                                    short yStart, COLORREF cTransparentColor,
326                                    bitmapType eType)
327 {
328     BITMAP     bm;
329     COLORREF   cColor;
330     HBITMAP    bmAndBack, bmAndObject, bmAndMem, bmSave;
331     HBITMAP    bmBackOld, bmObjectOld, bmMemOld, bmSaveOld;
332     HDC        hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave;
333     POINT      ptSize;
334     HBRUSH     hOldBrush;
335 
336     BOOL bRC;
337 
338     hdcTemp = CreateCompatibleDC(hDC);
339     SelectObject(hdcTemp, hBitmap);   // Select the bitmap
340 
341     GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bm);
342     ptSize.x = bm.bmWidth;            // Get width of bitmap
343     ptSize.y = bm.bmHeight;           // Get height of bitmap
344     DPtoLP(hdcTemp, &ptSize, 1);      // Convert from device
345                                       // to logical points
346 
347     // Create some DCs to hold temporary data.
348     hdcBack   = CreateCompatibleDC(hDC);
349     hdcObject = CreateCompatibleDC(hDC);
350     hdcMem    = CreateCompatibleDC(hDC);
351     hdcSave   = CreateCompatibleDC(hDC);
352 
353     // Create a bitmap for each DC. DCs are required for a number of
354     // GDI functions.
355 
356     // Monochrome DC
357     bmAndBack   = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
358 
359     // Monochrome DC
360     bmAndObject = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
361 
362     bmAndMem    = CreateCompatibleBitmap(hDC, ptSize.x, ptSize.y);
363     bmSave      = CreateCompatibleBitmap(hDC, ptSize.x, ptSize.y);
364 
365     // Each DC must select a bitmap object to store pixel data.
366     bmBackOld   = (HBITMAP)SelectObject(hdcBack, bmAndBack);
367     bmObjectOld = (HBITMAP)SelectObject(hdcObject, bmAndObject);
368     bmMemOld    = (HBITMAP)SelectObject(hdcMem, bmAndMem);
369     bmSaveOld   = (HBITMAP)SelectObject(hdcSave, bmSave);
370 
371     // Set proper mapping mode.
372     SetMapMode(hdcTemp, GetMapMode(hDC));
373 
374     // Save the bitmap sent here, because it will be overwritten.
375     bRC = BitBlt(hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);
376 
377     // Create an "AND mask" that contains the mask of the colors to draw
378     // (the nontransparent portions of the image).
379 
380     // Set the background color of the source DC to the color.
381     // contained in the parts of the bitmap that should be transparent
382     cColor = SetBkColor(hdcTemp, cTransparentColor);
383 
384     // Create the object mask for the bitmap by performing a BitBlt
385     // from the source bitmap to a monochrome bitmap.
386     bRC = BitBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);
387 
388     // Set the background color of the source DC back to the original color.
389     SetBkColor(hdcTemp, cColor);
390 
391     // Create the inverse of the object mask.
392     bRC = BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, NOTSRCCOPY);
393 
394     // Copy the background of the main DC to the destination.
395     bRC = BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hDC, xStart, yStart, SRCCOPY);
396 
397     // Mask out the places where the bitmap will be placed.
398     // hdcMem then contains the background color of hDC only in the places
399     // where the transparent pixels reside.
400     bRC = BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);
401 
402     if(eType == eNormal)
403     {
404         // Mask out the transparent colored pixels on the bitmap.
405         // hdcTemp then contains only the non-transparent pixels.
406         BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);
407 
408         // XOR the bitmap with the background on the destination DC.
409         // hdcMem then contains the required result.
410         BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);
411     }
412     else if(eType == eShadow)
413     {
414         //Select shadow brush into hdcTemp
415         hOldBrush = (HBRUSH)SelectObject(hdcTemp, m_hIconShadowBrush);
416 
417         //Copy shadow brush pixels for all non-transparent pixels to hdcTemp
418         bRC = BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, MERGECOPY);
419 
420         // XOR the bitmap with the background on the destination DC.
421         // hdcMem then contains the required result.
422         bRC = BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);
423 
424         //Restore the brush in hdcTemp
425         SelectObject(hdcTemp, hOldBrush);
426     }
427     else if(eType == eFaded)
428     {
429         COLORREF col;
430         int x, y;
431 
432         //Lighten the color of each pixel in hdcTemp
433         for(x=0; x<ptSize.x; x++)
434         {
435             for(y=0; y<ptSize.y; y++)
436             {
437                 col = GetPixel(hdcTemp, x, y);
438                 col = LightenColor(col, 0.3);
439                 SetPixel(hdcTemp, x, y, col);
440             }
441         }
442 
443         // Mask out the transparent colored pixels on the bitmap.
444         // hdcTemp then contains only the non-transparent pixels.
445         BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);
446 
447         // XOR the bitmap with the background on the destination DC.
448         // hdcMem then contains the required result.
449         BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);
450     }
451     else if(eType == eDisabled)
452     {
453         COLORREF discol, col;
454         BYTE r, g, b;
455         int x, y;
456         int avgcol;
457         double factor;
458 
459         //Lighten the color of COLOR_BTNSHADOW by a weighted average of the color at each pixel in hdcTemp.
460         //Set the pixel to the lightened color.
461         discol = GetSysColor(COLOR_BTNSHADOW);
462         for(x=0; x<ptSize.x; x++)
463         {
464             for(y=0; y<ptSize.y; y++)
465             {
466                 col = GetPixel(hdcTemp, x, y);
467                 r = GetRValue(col);
468 				g = GetGValue(col);
469 				b = GetBValue(col);
470                 avgcol = (r + g + b) / 3;
471 				factor = avgcol / 255.0;
472                 SetPixel(hdcTemp, x, y, LightenColor(discol, factor));
473             }
474         }
475 
476         // Mask out the transparent colored pixels on the bitmap.
477         // hdcTemp then contains only the non-transparent pixels.
478         BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);
479 
480         // XOR the bitmap with the background on the destination DC.
481         // hdcMem then contains the required result.
482         BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);
483     }
484 
485     // Copy the destination to the screen.
486     bRC = BitBlt(hDC, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0, SRCCOPY);
487 
488     // Place the original bitmap back into the bitmap sent here.
489     bRC = BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY);
490 
491     // Delete the memory bitmaps.
492     DeleteObject(SelectObject(hdcBack, bmBackOld));
493     DeleteObject(SelectObject(hdcObject, bmObjectOld));
494     DeleteObject(SelectObject(hdcMem, bmMemOld));
495     DeleteObject(SelectObject(hdcSave, bmSaveOld));
496 
497     // Delete the memory DCs.
498     DeleteDC(hdcMem);
499     DeleteDC(hdcBack);
500     DeleteDC(hdcObject);
501     DeleteDC(hdcSave);
502     DeleteDC(hdcTemp);
503 }
504 
DrawCheckMark(HDC hDC,short x,short y,bool bNarrow)505 void ODMenu::DrawCheckMark(HDC hDC, short x, short y, bool bNarrow)
506 {
507     HPEN hOldPen;
508     int dp = 0;
509 
510     if(bNarrow)
511         dp = 1;
512 
513     //Select check mark pen
514     hOldPen = (HPEN)SelectObject(hDC, m_hCheckMarkPen);
515 
516     //Draw the check mark
517     MoveToEx(hDC, x, y + 2, NULL);
518 	LineTo(hDC, x, y + 5 - dp);
519 
520 	MoveToEx(hDC, x + 1, y + 3, NULL);
521 	LineTo(hDC, x + 1, y + 6 - dp);
522 
523 	MoveToEx(hDC, x + 2, y + 4, NULL);
524 	LineTo(hDC, x + 2, y + 7 - dp);
525 
526 	MoveToEx(hDC, x + 3, y + 3, NULL);
527 	LineTo(hDC, x + 3, y + 6 - dp);
528 
529 	MoveToEx(hDC, x + 4, y + 2, NULL);
530 	LineTo(hDC, x + 4, y + 5 - dp);
531 
532 	MoveToEx(hDC, x + 5, y + 1, NULL);
533 	LineTo(hDC, x + 5, y + 4 - dp);
534 
535 	MoveToEx(hDC, x + 6, y, NULL);
536 	LineTo(hDC, x + 6, y + 3 - dp);
537 
538     //Restore original DC pen
539     SelectObject(hDC, hOldPen);
540 }
541 
LightenColor(COLORREF col,double factor)542 COLORREF ODMenu::LightenColor(COLORREF col, double factor)
543 {
544 	if(factor > 0.0 && factor <= 1.0)
545     {
546 		BYTE red, green, blue, lightred, lightgreen, lightblue;
547 		red = GetRValue(col);
548 		green = GetGValue(col);
549 		blue = GetBValue(col);
550 		lightred = (BYTE)((factor*(255 - red)) + red);
551 		lightgreen = (BYTE)((factor*(255 - green)) + green);
552 		lightblue = (BYTE)((factor*(255 - blue)) + blue);
553 		col = RGB(lightred, lightgreen, lightblue);
554 	}
555 
556 	return col;
557 }
558 
DarkenColor(COLORREF col,double factor)559 COLORREF ODMenu::DarkenColor(COLORREF col, double factor)
560 {
561 	if(factor > 0.0 && factor <= 1.0)
562     {
563 		BYTE red, green, blue, lightred, lightgreen, lightblue;
564 		red = GetRValue(col);
565 		green = GetGValue(col);
566 		blue = GetBValue(col);
567 		lightred = (BYTE)(red - (factor*red));
568 		lightgreen = (BYTE)(green - (factor*green));
569 		lightblue = (BYTE)(blue - (factor*blue));
570 		col = RGB(lightred, lightgreen, lightblue);
571 	}
572 	return col;
573 }
574 
AverageColor(COLORREF col1,COLORREF col2,double weight)575 COLORREF ODMenu::AverageColor(COLORREF col1, COLORREF col2, double weight)
576 {
577     BYTE avgRed, avgGreen, avgBlue;
578 
579     if (weight <= 0.0)
580         return col1;
581 	else if (weight > 1.0)
582 		return col2;
583 
584     avgRed   = (BYTE) (GetRValue(col1) * weight + GetRValue(col2) * (1.0 - weight));
585     avgGreen = (BYTE) (GetGValue(col1) * weight + GetGValue(col2) * (1.0 - weight));
586     avgBlue  = (BYTE) (GetBValue(col1) * weight + GetBValue(col2) * (1.0 - weight));
587 
588     return RGB(avgRed, avgGreen, avgBlue);
589 }
590 
GetColorIntensity(COLORREF col)591 double ODMenu::GetColorIntensity(COLORREF col)
592 {
593     BYTE red, green, blue;
594 
595     red = GetRValue(col);
596     green = GetGValue(col);
597 	blue = GetBValue(col);
598 
599     //denominator of 765 is (255*3)
600     return (double)red/765.0 + (double)green/765.0 + (double)blue/765.0;
601 }
602 
MeasureItem(HWND hWnd,LPARAM lParam)603 void ODMenu::MeasureItem(HWND hWnd, LPARAM lParam)
604 {
605     MEASUREITEMSTRUCT* lpmis = (MEASUREITEMSTRUCT*)lParam;
606     ODMENUITEMS::iterator it;
607     ODMENUITEM item;
608     HDC hDC;
609     HFONT hfntOld;
610     RECT rect;
611 
612     it = m_menuItems.find(lpmis->itemData);
613     if(it == m_menuItems.end())
614         return;
615 
616     hDC = GetDC(hWnd);
617     hfntOld = (HFONT)SelectObject(hDC, m_hFont);
618 
619     item = it->second;
620     if(item.displayText.length() > 0)
621     {
622         DrawText(hDC, item.rawText.c_str(), item.rawText.length(), &rect,
623             DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT);
624         lpmis->itemWidth = rect.right - rect.left;
625 
626         if(!item.topMost)
627         {
628             //Correct size for drop down menus
629             lpmis->itemWidth += (m_iconWidth + 2*m_iconBarMargin + m_textLeftMargin + m_textRightMargin);
630             lpmis->itemHeight += m_verticalSpacing;
631         }
632      }
633     else if(item.dwType & MFT_SEPARATOR)
634     {
635         //Correct size for drop down menus
636         if(!item.topMost)
637         {
638             lpmis->itemWidth += (m_iconWidth + 2*m_iconBarMargin + m_textLeftMargin + m_textRightMargin);
639             lpmis->itemHeight = 3;
640         }
641     }
642 
643     SelectObject(hDC, hfntOld);
644     ReleaseDC(hWnd, hDC);
645 }
646 
DrawItem(HWND hWnd,LPARAM lParam)647 void ODMenu::DrawItem(HWND hWnd, LPARAM lParam)
648 {
649     DRAWITEMSTRUCT* lpdis = (DRAWITEMSTRUCT*)lParam;
650     ODMENUITEMS::iterator it;
651     ODMENUITEM item;
652     COLORREF clrPrevText, clrPrevBkgnd;
653     HFONT hPrevFnt;
654     HPEN hPrevPen;
655     HBRUSH hPrevBrush;
656 
657     it = m_menuItems.find(lpdis->itemData);
658     if(it == m_menuItems.end())
659         return;
660 
661     item = it->second;
662 
663     //Draw based on type of item
664     if(item.displayText.length() > 0)
665     {
666         // Set the appropriate foreground and background colors.
667         if(item.topMost)
668         {
669             if(lpdis->itemState & ODS_SELECTED)
670             {
671                 clrPrevText = SetTextColor(lpdis->hDC, m_clrHighlightItemText);
672                 clrPrevBkgnd = SetBkColor(lpdis->hDC, m_clrHighlightItemBackground);
673             }
674             else
675             {
676                 clrPrevText = SetTextColor(lpdis->hDC, m_clrItemText);
677                 clrPrevBkgnd = SetBkColor(lpdis->hDC, GetSysColor(COLOR_MENU));
678             }
679         }
680         else
681         {
682             if(lpdis->itemState & ODS_GRAYED || lpdis->itemState & ODS_DISABLED)
683             {
684                 clrPrevText = SetTextColor(lpdis->hDC, GetSysColor(COLOR_3DSHADOW));
685                 clrPrevBkgnd = SetBkColor(lpdis->hDC, m_clrItemBackground);
686             }
687             else if(lpdis->itemState & ODS_SELECTED)
688             {
689                 clrPrevText = SetTextColor(lpdis->hDC, m_clrHighlightItemText);
690                 clrPrevBkgnd = SetBkColor(lpdis->hDC, m_clrHighlightItemBackground);
691             }
692             else
693             {
694                 clrPrevText = SetTextColor(lpdis->hDC, m_clrItemText);
695                 clrPrevBkgnd = SetBkColor(lpdis->hDC, m_clrItemBackground);
696             }
697         }
698 
699         // Select the font.
700         hPrevFnt = (HFONT)SelectObject(lpdis->hDC, m_hFont);
701 
702         //Draw the text
703         DrawItemText(lpdis, item);
704 
705         //Restore original font
706         SelectObject(lpdis->hDC, hPrevFnt);
707 
708         SetTextColor(lpdis->hDC, clrPrevText);
709         SetBkColor(lpdis->hDC, clrPrevBkgnd);
710     }
711     else if(item.dwType & MFT_SEPARATOR)
712     {
713 		//Fill menu space with menu background, first.
714         RECT rect;
715         memcpy(&rect, &lpdis->rcItem, sizeof(RECT));
716         rect.left += (m_iconWidth + 2*m_iconBarMargin);
717         FillRect(lpdis->hDC, &rect, m_hItemBackground);
718 
719         //Draw the separator line
720         hPrevPen = (HPEN)SelectObject(lpdis->hDC, m_hSeparatorPen);
721         MoveToEx(lpdis->hDC, lpdis->rcItem.left + m_iconWidth + 2*m_iconBarMargin + m_textLeftMargin,
722             lpdis->rcItem.top+1, NULL);
723         LineTo(lpdis->hDC, lpdis->rcItem.right, lpdis->rcItem.top+1);
724 
725         //Restore GDI objects in DC
726         SelectObject(lpdis->hDC, hPrevPen);
727     }
728 
729     //Draw the left icon bar
730     DrawIconBar(lpdis, item);
731 
732     //Draw selection outline if drawing a selected item
733     if(lpdis->itemState & ODS_SELECTED && !(lpdis->itemState & ODS_GRAYED || lpdis->itemState & ODS_DISABLED))
734     {
735         hPrevBrush = (HBRUSH)SelectObject(lpdis->hDC, (HBRUSH)GetStockObject(NULL_BRUSH));
736         hPrevPen = (HPEN)SelectObject(lpdis->hDC, m_hSelectionOutlinePen);
737         Rectangle(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top,
738             lpdis->rcItem.right, lpdis->rcItem.bottom);
739 
740         //Restore GDI objects in DC
741         SelectObject(lpdis->hDC, hPrevBrush);
742         SelectObject(lpdis->hDC, hPrevPen);
743     }
744 }
745 
OnDestroy()746 void ODMenu::OnDestroy()
747 {
748 }
749 
GetItem(UINT id,ODMENUITEM ** ppItem)750 bool ODMenu::GetItem(UINT id, ODMENUITEM** ppItem)
751 {
752     bool bRC = true;
753 
754     if(!ppItem)
755         return false;
756 
757     ODMENUITEMS::iterator it = m_menuItems.find(id);
758     if(it != m_menuItems.end())
759         *ppItem = &it->second;
760 
761     return bRC;
762 }
763 
SetItemImage(HINSTANCE hInst,UINT wID,UINT idBitmap)764 void ODMenu::SetItemImage(HINSTANCE hInst, UINT wID, UINT idBitmap)
765 {
766     // Get iterator to ODMENUITEM
767     ODMENUITEMS::iterator it;
768     HBITMAP hBitmap;
769 
770     // Load the bitmap resource
771     hBitmap = (HBITMAP) LoadImage(hInst,
772                                   MAKEINTRESOURCE(idBitmap),
773                                   IMAGE_BITMAP, 0, 0, LR_SHARED);
774     if (hBitmap)
775     {
776         // Find menu item having specified wID.
777         it = m_menuItems.begin();
778         while(it != m_menuItems.end())
779         {
780             if(it->second.wID == wID)
781             {
782                 it->second.hBitmap = hBitmap;
783                 break;
784             }
785 
786             it++;
787         }
788     }
789     else
790     {
791         DWORD dwErr = GetLastError();
792         dwErr = 0;
793     }
794 }
795 
AddItem(HMENU hMenu,int index,MENUITEMINFO * pItemInfo)796 void ODMenu::AddItem(HMENU hMenu, int index, MENUITEMINFO* pItemInfo)
797 {
798     MENUITEMINFO miInfo;
799     ODMENUITEM odInfo;
800 
801     //Obtain menu item info if not supplied
802     if(!pItemInfo)
803     {
804         miInfo.cbSize = sizeof(MENUITEMINFO);
805         miInfo.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_ID;
806         miInfo.dwTypeData = m_szItemText;
807         miInfo.cch = sizeof(m_szItemText);
808 
809         if(GetMenuItemInfo(hMenu, index, TRUE, &miInfo))
810             pItemInfo = &miInfo;
811     }
812 
813     //Add menu info to map of menu items
814     if(pItemInfo)
815     {
816         if(pItemInfo->hSubMenu != NULL && hMenu == m_hRootMenu)
817             odInfo.topMost = true;
818         else
819             odInfo.topMost = false;
820         if(pItemInfo->fType == MFT_STRING)
821         {
822             odInfo.rawText = pItemInfo->dwTypeData;
823             GenerateDisplayText(odInfo);
824         }
825         else
826         {
827             odInfo.rawText = "";
828             odInfo.displayText = "";
829         }
830         odInfo.dwType = pItemInfo->fType;
831         odInfo.wID = pItemInfo->wID;
832         odInfo.hBitmap = NULL;
833         m_menuItems[m_seqNumber] = odInfo;
834         SetMenuItemOwnerDrawn(hMenu, index, pItemInfo->fType);
835     }
836 }
837 
DeleteItem(HMENU hMenu,int index)838 void ODMenu::DeleteItem(HMENU hMenu, int index)
839 {
840     //Item data for item is map index
841     MENUITEMINFO miInfo;
842 
843     miInfo.cbSize = sizeof(MENUITEMINFO);
844     miInfo.fMask = MIIM_SUBMENU | MIIM_DATA;
845     if(GetMenuItemInfo(hMenu, index, TRUE, &miInfo))
846     {
847         if(miInfo.hSubMenu)
848             DeleteSubMenu(miInfo.hSubMenu);
849 
850         //Remove this item from map
851         m_menuItems.erase(miInfo.dwItemData);
852     }
853 }
854