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