1 /* AbiSource Program Utilities
2 * Copyright (C) 1998 AbiSource, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 /*
21 In versions previous to Windows Vista we use owner-draw menus.
22 In Vista we use SetMenuItemBitmaps that is more powerful that previous Windows editions
23 */
24
25 #include <wchar.h>
26 #include <windows.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include "ut_types.h"
32 #include "ut_stack.h"
33 #include "ut_string.h"
34 #include "ut_string_class.h"
35 #include "ut_debugmsg.h"
36 #include "ut_Language.h"
37 #include "xap_Types.h"
38 #include "ev_Win32Menu.h"
39 #include "xap_Win32App.h"
40 #include "ev_Menu_Layouts.h"
41 #include "ev_Menu_Actions.h"
42 #include "ev_Menu_Labels.h"
43 #include "ev_EditEventMapper.h"
44 #include "xap_Frame.h"
45 #include "ap_Win32Resources.rc2"
46 #include "ap_Menu_Id.h"
47 #include "xap_App.h"
48 #include "xap_Win32App.h"
49 #include "xap_Win32Toolbar_Icons.h"
50 #include "ap_Win32App.h"
51 #include "ut_Win32OS.h"
52 #include "ut_Win32LocaleString.h"
53
54 #define SPACE_ICONTEXT 4 // Pixels between the icon and the text
55 #define BITMAP_WITDH 16
56 #define BITMAP_HEIGHT 16
57
58
59 /*
60 This table assigns an icon to every menu entry
61 */
62 static const EV_Menu_Bitmap s_bitmaps[] =
63 {
64
65
66 // File
67 {AP_MENU_ID_FILE_CLOSE, "Menu_AbiWord_Close"},
68 {AP_MENU_ID_FILE_EXIT, "Menu_AbiWord_Exit"},
69 {AP_MENU_ID_FILE_EXPORT, "Menu_AbiWord_Export"},
70 {AP_MENU_ID_FILE_IMPORT, "Menu_AbiWord_Import"},
71 {AP_MENU_ID_FILE_NEW, "Menu_AbiWord_New"},
72 {AP_MENU_ID_FILE_OPEN, "Menu_AbiWord_Open"},
73 {AP_MENU_ID_FILE_PRINT, "Menu_AbiWord_Print"},
74 {AP_MENU_ID_FILE_SAVE, "Menu_AbiWord_Save"},
75 {AP_MENU_ID_FILE_SAVEAS, "Menu_AbiWord_SaveAs"},
76 {AP_MENU_ID_FILE_REVERT, "Menu_AbiWord_Revert"},
77 {AP_MENU_ID_FILE_PROPERTIES, "Menu_AbiWord_Properties"},
78 {AP_MENU_ID_FILE_PAGESETUP, "Menu_AbiWord_Print_Setup"},
79
80 // Edit
81 {AP_MENU_ID_EDIT_PASTE, "Menu_AbiWord_Paste"},
82 {AP_MENU_ID_EDIT_CUT, "Menu_AbiWord_Cut"},
83 {AP_MENU_ID_EDIT_COPY, "Menu_AbiWord_Copy"},
84 {AP_MENU_ID_EDIT_UNDO, "Menu_AbiWord_Undo"},
85 {AP_MENU_ID_EDIT_REDO, "Menu_AbiWord_Redo"},
86 {AP_MENU_ID_EDIT_PASTE_SPECIAL, "Menu_AbiWord_Paste"},
87 {AP_MENU_ID_EDIT_CLEAR, "Menu_AbiWord_Clear"},
88 {AP_MENU_ID_EDIT_FIND, "Menu_AbiWord_Search"},
89 {AP_MENU_ID_EDIT_REPLACE, "Menu_AbiWord_Replace"},
90 {AP_MENU_ID_EDIT_GOTO, "Menu_AbiWord_Goto"},
91
92 // Insert
93 {AP_MENU_ID_INSERT_SYMBOL, "Menu_AbiWord_Insert_Symbol"},
94 {AP_MENU_ID_INSERT_GRAPHIC, "Menu_AbiWord_Img"},
95 {AP_MENU_ID_INSERT_HYPERLINK, "Menu_AbiWord_Hyperlink"},
96
97 // Format
98 {AP_MENU_ID_FMT_FONT, "Menu_AbiWord_Font"},
99 {AP_MENU_ID_TABLE_INSERT_TABLE, "Menu_AbiWord_Insert_Table"},
100 {AP_MENU_ID_TOOLS_SPELL, "Menu_AbiWord_Spellcheck"},
101 {AP_MENU_ID_TOOLS_OPTIONS, "Menu_AbiWord_Preferences"},
102 {AP_MENU_ID_TOOLS_SCRIPTS, "Menu_AbiWord_Execute"},
103 {AP_MENU_ID_TOOLS_LANGUAGE, "Menu_AbiWord_Book"},
104 {AP_MENU_ID_FMT_LANGUAGE, "Menu_AbiWord_Book"},
105
106
107 // Table
108 {AP_MENU_ID_TABLE_INSERT_TABLE, "Menu_AbiWord_Insert_Table"},
109 {AP_MENU_ID_TABLE_DELETE_TABLE, "Menu_AbiWord_Delete_Table"},
110 {AP_MENU_ID_TABLE_INSERTTABLE, "Menu_AbiWord_Insert_Table"},
111 {AP_MENU_ID_TABLE_DELETETABLE, "Menu_AbiWord_Delete_Table"},
112 {AP_MENU_ID_TABLE_MERGE_CELLS, "Menu_AbiWord_Merge_Cells"},
113 {AP_MENU_ID_TABLE_SPLIT_CELLS, "Menu_AbiWord_Split_Cells"},
114 {AP_MENU_ID_TABLE_INSERTCOLUMN, "Menu_AbiWord_Add_Column"},
115 {AP_MENU_ID_TABLE_INSERTROW, "Menu_AbiWord_Add_Row"},
116 {AP_MENU_ID_TABLE_DELETECOLUMN, "Menu_AbiWord_Delete_Column"},
117 {AP_MENU_ID_TABLE_DELETEROW, "Menu_AbiWord_Delete_Row"},
118 {AP_MENU_ID_TABLE_DELETE_COLUMNS, "Menu_AbiWord_Delete_Column"},
119 {AP_MENU_ID_TABLE_DELETE_ROWS, "Menu_AbiWord_Delete_Row"},
120
121
122 // Help
123 {AP_MENU_ID_HELP_SEARCH, "Menu_AbiWord_Search"},
124 {AP_MENU_ID_HELP_ABOUT, "Menu_AbiWord_About"},
125 {AP_MENU_ID_HELP_CREDITS, "Menu_AbiWord_Credits"},
126
127 {0, ""}
128
129
130 };
131
132
133
134 /*
135
136 */
_ev_GetLabelName(XAP_Win32App * pWin32App,XAP_Frame *,const EV_EditEventMapper * pEEM,const EV_Menu_Action * pAction,EV_Menu_Label * pLabel)137 static const wchar_t * _ev_GetLabelName(XAP_Win32App * pWin32App,
138 XAP_Frame * /*pFrame*/,
139 const EV_EditEventMapper * pEEM,
140 const EV_Menu_Action * pAction,
141 EV_Menu_Label * pLabel)
142 {
143 const wchar_t * szLabelName;
144 const char * szLabelName_utf8;
145
146 if (pAction->hasDynamicLabel())
147 szLabelName_utf8 = pAction->getDynamicLabel(pLabel);
148 else
149 szLabelName_utf8 = pLabel->getMenuLabel();
150
151 if (!szLabelName_utf8 || !*szLabelName_utf8)
152 return NULL;
153
154 UT_Win32LocaleString str;
155 str.fromUTF8 (szLabelName_utf8);
156 szLabelName = str.c_str();
157
158 const char * szShortcut_ascii = NULL;
159 const wchar_t * szShortcut = NULL;
160 UT_Win32LocaleString shortcut;
161
162 int len = 0;
163
164 if (pEEM)
165 {
166 // see if this has an associated keybinding
167 const char * szMethodName = pAction->getMethodName();
168
169 if (szMethodName)
170 {
171 const EV_EditMethodContainer * pEMC = pWin32App->getEditMethodContainer();
172 UT_return_val_if_fail(pEMC, NULL);
173
174 EV_EditMethod * pEM = pEMC->findEditMethodByName(szMethodName);
175 UT_ASSERT(pEM); // make sure it's bound to something
176
177 szShortcut_ascii = pEEM->getShortcutFor(pEM);
178 if (szShortcut_ascii && *szShortcut_ascii)
179 len = strlen(szShortcut_ascii) + 1; // extra char is for the tab
180 }
181 }
182
183 if (szShortcut && *szShortcut) {
184 shortcut.fromASCII (szShortcut_ascii);
185 }
186
187 if (pAction->raisesDialog())
188 len += 4;
189
190 if (!len)
191 {
192 static wchar_t buf[128];
193 wcscpy(buf,szLabelName);
194 return buf;
195 }
196
197 static wchar_t buf[128];
198 int l = G_N_ELEMENTS(buf);
199 memset(buf,0,l);
200 wcscpy(buf,szLabelName); //strncpy
201
202 // append "..." to menu item if it raises a dialog
203 if (pAction->raisesDialog())
204 wcscat(buf, L"..."); //todo
205
206 // append shortcut mnemonic, if any
207 if (shortcut.length())
208 {
209 wcscat(buf, L"\t"); //todo
210 wcscat(buf, shortcut.c_str());
211 }
212
213 return buf;
214 }
215
216
217
218 /*****************************************************************/
219
EV_Win32Menu(XAP_Win32App * pWin32App,const EV_EditEventMapper * pEEM,const char * szMenuLayoutName,const char * szMenuLabelSetName)220 EV_Win32Menu::EV_Win32Menu(XAP_Win32App * pWin32App,
221 const EV_EditEventMapper * pEEM,
222 const char * szMenuLayoutName,
223 const char * szMenuLabelSetName)
224 : EV_Menu(pWin32App, pWin32App->getEditMethodContainer(), szMenuLayoutName, szMenuLabelSetName),
225 m_pWin32App(pWin32App),
226 m_pEEM(pEEM),
227 m_myMenu(NULL),
228 m_iDIR(0)
229 {
230
231 NONCLIENTMETRICSA ncm;
232 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
233 m_bTrack = false;
234
235 SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0);
236 m_hFont = CreateFontIndirectA(&ncm.lfMenuFont);
237
238 m_nBitmapCX = BITMAP_WITDH + 2;
239 m_nBitmapCY = BITMAP_HEIGHT + 2;
240
241 UT_return_if_fail( m_pMenuLabelSet );
242
243 UT_Language l;
244 if(l.getDirFromCode(m_pMenuLabelSet->getLanguage()) == UTLANG_RTL)
245 m_iDIR = DT_RTLREADING;
246 }
247
destroy()248 void EV_Win32Menu::destroy()
249 {
250
251 UT_VECTOR_PURGEALL(EV_Menu_Item*,m_vecItems);
252
253 if (m_myMenu)
254 {
255 DestroyMenu(m_myMenu);
256 m_myMenu = 0;
257 }
258
259 if (m_hFont)
260 DeleteObject(m_hFont);
261
262 }
263
~EV_Win32Menu()264 EV_Win32Menu::~EV_Win32Menu()
265 {
266 // we let the derived classes handle destruction of m_myMenu if appropriate.
267 // TODO: this is never colld
268
269 for (std::vector<HBITMAP>::const_iterator i = m_vechBitmaps.begin();
270 i != m_vechBitmaps.end(); i++)
271 {
272 DeleteObject (*i);
273 }
274
275 }
276
onCommand(AV_View * pView,HWND,WPARAM wParam)277 bool EV_Win32Menu::onCommand(AV_View * pView,
278 HWND /*hWnd*/, WPARAM wParam)
279 {
280 // TODO do we need the hWnd parameter....
281
282 // map the windows WM_COMMAND command-id into one of our XAP_Menu_Id.
283 // we don't need to range check it, getAction() will puke if it's
284 // out of range.
285
286 XAP_Menu_Id id = MenuIdFromWmCommand(LOWORD(wParam));
287
288 // user selected something from the menu.
289 // invoke the appropriate function.
290 // return true iff handled.
291
292 const EV_Menu_ActionSet * pMenuActionSet = m_pWin32App->getMenuActionSet();
293 UT_return_val_if_fail(pMenuActionSet, false);
294
295 const EV_Menu_Action * pAction = pMenuActionSet->getAction(id);
296 if (!pAction)
297 return false;
298
299 const char * szMethodName = pAction->getMethodName();
300 UT_return_val_if_fail(szMethodName, false);
301
302 const EV_EditMethodContainer * pEMC = m_pWin32App->getEditMethodContainer();
303 UT_return_val_if_fail(pEMC, false);
304
305 EV_EditMethod * pEM = pEMC->findEditMethodByName(szMethodName);
306 UT_ASSERT(pEM); // make sure it's bound to something
307
308 UT_String script_name(pAction->getScriptName());
309 invokeMenuMethod(pView, pEM, script_name);
310 return true;
311 }
312
313 /*
314
315 Create a Win32 menu from the info provided.
316
317 */
synthesizeMenu(XAP_Frame * pFrame,HMENU menuRoot)318 bool EV_Win32Menu::synthesizeMenu(XAP_Frame * pFrame, HMENU menuRoot)
319 {
320 UT_DebugOnly<bool> bResult;
321
322 const EV_Menu_ActionSet * pMenuActionSet = m_pWin32App->getMenuActionSet();
323 UT_ASSERT(pMenuActionSet);
324
325 const UT_uint32 nrLabelItemsInLayout = m_pMenuLayout->getLayoutItemCount();
326 UT_ASSERT(nrLabelItemsInLayout > 0);
327
328 // we keep a stack of the submenus so that we can properly
329 // parent the menu items and deal with nested pull-rights.
330
331 UT_Stack stack;
332 stack.push(menuRoot);
333 //UT_DEBUGMSG(("Menu::synthesize [menuRoot 0x%08lx]\n",menuRoot));
334
335 for (UT_uint32 k=0; (k < nrLabelItemsInLayout); k++)
336 {
337
338 EV_Menu_LayoutItem * pLayoutItem = m_pMenuLayout->getLayoutItem(k);
339 UT_continue_if_fail(pLayoutItem);
340
341 XAP_Menu_Id id = pLayoutItem->getMenuId();
342 const EV_Menu_Action * pAction = pMenuActionSet->getAction(id);
343 UT_ASSERT(pAction);
344 EV_Menu_Label * pLabel = m_pMenuLabelSet->getLabel(id);
345 UT_ASSERT(pLabel);
346
347 // get the name for the menu item
348
349 const wchar_t * szLabelName = _ev_GetLabelName(m_pWin32App,pFrame,m_pEEM,pAction,pLabel);
350
351 switch (pLayoutItem->getMenuLayoutFlags())
352 {
353 case EV_MLF_BeginSubMenu:
354 UT_ASSERT(szLabelName && *szLabelName);
355 // Fall Thru Intended
356
357 case EV_MLF_Normal:
358 {
359 UT_DEBUGMSG(("menu::synthesize [name %s]\n",szLabelName));
360
361 HMENU m;
362 bResult = stack.viewTop((void **)&m);
363 UT_ASSERT(bResult);
364
365 // set standard flags on the item, we'll update the
366 // state on an onInitMenu().
367 // map our XAP_Menu_Id into a windows WM_COMMAND id.
368
369 UINT flags = MF_STRING | MF_ENABLED | MF_UNCHECKED;
370 UINT u = WmCommandFromMenuId(id);
371
372 /*
373 This a menu bar
374 */
375 if (pLayoutItem->getMenuLayoutFlags() == EV_MLF_BeginSubMenu)
376 {
377 HMENU sub = CreateMenu(); // TODO NOTE: Leaking handle!
378 UT_ASSERT(sub);
379
380 UT_DEBUGMSG(("menu::synthesize [name %s][subMenu 0x%08lx] %u\n",szLabelName,u, stack.getDepth()));
381
382 flags |= MF_POPUP;
383 stack.push(sub);
384 u = (size_t) sub;
385
386 if (szLabelName && *szLabelName)
387 {
388 EV_Menu_Item* item = new EV_Menu_Item;
389 item->id = id;
390 item->pMenu= this;
391
392 wcscpy (item->szText, szLabelName);
393 m_vecItems.addItem(item);
394
395 if (!m_bTrack && stack.getDepth()==2)
396 AppendMenuW(m, flags,u, szLabelName);
397 else {
398 if (UT_IsWinVista()) {
399 AppendMenuW(m, flags|MF_STRING,u, szLabelName);
400 _setBitmapforID(m, id, u);
401 }
402 else
403 AppendMenuW(m, flags|MF_OWNERDRAW,u, (const wchar_t*) item);
404 }
405 }
406
407 }
408 else
409 {
410 if (szLabelName && *szLabelName)
411 {
412 EV_Menu_Item* item = new EV_Menu_Item;
413 item->id = id;
414 item->pMenu= this;
415 wcscpy (item->szText, szLabelName);
416 m_vecItems.addItem(item);
417
418 if (UT_IsWinVista()) { //todo: code for windows vista : investigate this
419 AppendMenuW(m, MF_STRING, u, szLabelName);
420 _setBitmapforID (m, id, u);
421 }
422 else
423 AppendMenuW(m, MF_OWNERDRAW, u, (const wchar_t*) item);
424 }
425 }
426
427
428 }
429 break;
430
431 case EV_MLF_EndSubMenu:
432 {
433 HMENU m = NULL;
434 bResult = stack.pop((void **)&m);
435 UT_ASSERT(bResult);
436 UT_DEBUGMSG(("menu::synthesize [endSubMenu 0x%08lx]\n",m));
437 }
438 break;
439
440 case EV_MLF_Separator:
441 {
442 HMENU m;
443 bResult = stack.viewTop((void **)&m);
444 UT_ASSERT(bResult);
445 UT_ASSERT(m);
446
447 AppendMenuW(m, MF_SEPARATOR, 0, NULL);
448 UT_DEBUGMSG(("menu::synthesize [separator appended to submenu 0x%08lx]\n",m));
449 }
450 break;
451
452 case EV_MLF_BeginPopupMenu:
453 case EV_MLF_EndPopupMenu:
454 break;
455
456 default:
457 UT_ASSERT_HARMLESS(0);
458 break;
459 }
460 }
461
462 #ifdef DEBUG
463 HMENU wDbg = NULL;
464 bResult = stack.pop((void **)&wDbg);
465 UT_ASSERT(bResult);
466 UT_ASSERT(wDbg == menuRoot);
467 #endif
468
469 return true;
470 }
471
onInitMenu(XAP_Frame * pFrame,AV_View * pView,HWND,HMENU hMenuBar)472 bool EV_Win32Menu::onInitMenu(XAP_Frame * pFrame, AV_View * pView, HWND /*hWnd*/, HMENU hMenuBar)
473 {
474 // deal with WM_INITMENU.
475
476 if (hMenuBar != m_myMenu) // these are not different when they
477 return false; // right-click on us on the menu bar.
478
479 const EV_Menu_ActionSet * pMenuActionSet = m_pWin32App->getMenuActionSet();
480 UT_uint32 nrLabelItemsInLayout = m_pMenuLayout->getLayoutItemCount();
481
482
483 UT_uintptr pos = 0;
484 UT_DebugOnly<bool> bResult;
485 UT_Stack stackPos;
486 stackPos.push((void*)pos);
487 UT_Stack stackMenu;
488 stackMenu.push(hMenuBar);
489
490 HMENU mTemp;
491 HMENU m = hMenuBar;
492 //UT_DEBUGMSG(("menu::onInitMenu: [menubar 0x%08lx]\n",m));
493
494 for (UT_uint32 k=0; (k < nrLabelItemsInLayout); k++)
495 {
496 EV_Menu_LayoutItem * pLayoutItem = m_pMenuLayout->getLayoutItem(k);
497 XAP_Menu_Id id = pLayoutItem->getMenuId();
498 const EV_Menu_Action * pAction = pMenuActionSet->getAction(id);
499 EV_Menu_Label * pLabel = m_pMenuLabelSet->getLabel(id);
500
501 UINT cmd = WmCommandFromMenuId(id);
502
503 switch (pLayoutItem->getMenuLayoutFlags())
504 {
505 case EV_MLF_Normal:
506 {
507 // see if we need to enable/disable and/or check/uncheck it.
508
509 UINT uEnable = MF_BYCOMMAND | MF_ENABLED;
510 UINT uCheck = MF_BYCOMMAND | MF_UNCHECKED;
511 UINT uBold = 0;
512 if (pAction->hasGetStateFunction())
513 {
514 EV_Menu_ItemState mis = pAction->getMenuItemState(pView);
515 if (mis & EV_MIS_Gray)
516 uEnable |= MF_GRAYED;
517 if (mis & EV_MIS_Toggled)
518 uCheck |= MF_CHECKED;
519 if (mis & EV_MIS_Bold)
520 uBold = MFS_DEFAULT;
521 }
522
523 if (!pAction->hasDynamicLabel())
524 {
525 // if no dynamic label, all we need to do
526 // is enable/disable and/or check/uncheck it.
527 pos++;
528
529 // need to check before disabling ...
530 CheckMenuItem(hMenuBar,cmd,uCheck);
531 EnableMenuItem(hMenuBar,cmd,uEnable);
532 break;
533 }
534
535 // get the current menu info for this item.
536
537 MENUITEMINFOW mif;
538 wchar_t bufMIF[128];
539 mif.cbSize = sizeof(mif);
540 mif.dwTypeData = bufMIF;
541 mif.cch = G_N_ELEMENTS(bufMIF)-1;
542 mif.fMask = MIIM_STATE | MIIM_TYPE | MIIM_ID;
543 BOOL bPresent = GetMenuItemInfoW(hMenuBar,cmd,FALSE,&mif);
544
545 // this item has a dynamic label...
546 // compute the value for the label.
547 // if it is blank, we remove the item from the menu.
548
549 const wchar_t * szLabelName = _ev_GetLabelName(m_pWin32App,pFrame,m_pEEM,pAction,pLabel);
550
551 BOOL bRemoveIt = (!szLabelName || !*szLabelName);
552
553 if (bRemoveIt) // we don't want it to be there
554 {
555 if (bPresent)
556 RemoveMenu(hMenuBar,cmd,MF_BYCOMMAND);
557
558 break;
559 }
560
561 // we want the item in the menu.
562 pos++;
563
564 if (bPresent) // just update the label on the item.
565 {
566 // dynamic label has changed
567 XAP_Menu_Id id = MenuIdFromWmCommand(cmd);
568 EV_Menu_Item* item;
569 for(UT_sint32 i = 0; i< m_vecItems.getItemCount(); i++)
570 {
571 item = (EV_Menu_Item*)m_vecItems.getNthItem(i);
572 if (id==item->id)
573 {
574 wcscpy (item->szText, szLabelName);
575 //UT_DEBUGMSG(("Menu changing text->%s\n",szLabelName));
576 if (UT_IsWinVista())
577 {
578 mif.fState = uCheck | uEnable | uBold;
579 mif.fType = MFT_STRING;
580 mif.dwTypeData = (LPWSTR)szLabelName;
581 SetMenuItemInfoW (hMenuBar,cmd,FALSE,&mif);
582 }
583 break;
584 }
585 }
586 }
587 else
588 {
589 EV_Menu_Item* item = new EV_Menu_Item;
590 item->id = id;
591 item->pMenu= this;
592 wcscpy (item->szText, szLabelName);
593 m_vecItems.addItem(item);
594 //UT_DEBUGMSG(("Menu adding menu->%s\n",szLabelName));
595
596 if (UT_IsWinVista()) {
597 AppendMenuW(m, MF_STRING, cmd, szLabelName);
598 _setBitmapforID(m, id, cmd);
599 }
600 else
601 AppendMenuW(m, MF_OWNERDRAW,cmd, (const wchar_t*) item);
602 }
603
604 EV_Menu_ItemState mis = pAction->getMenuItemState(pView);
605
606 if (mis & EV_MIS_Gray)
607 uEnable |= MF_GRAYED;
608
609 if (mis & EV_MIS_Toggled)
610 uCheck |= MF_CHECKED;
611
612 EnableMenuItem(m,cmd,uEnable);
613 CheckMenuItem(m,cmd,uCheck);
614
615 }
616 break;
617
618 case EV_MLF_Separator:
619 pos++;
620 break;
621
622 case EV_MLF_BeginSubMenu:
623 mTemp = m;
624 pos++;
625 stackMenu.push(mTemp);
626 stackPos.push((void*)pos);
627
628 m = GetSubMenu(mTemp, pos-1);
629 //UT_DEBUGMSG(("menu::onInitMenu: [menu 0x%08lx] at [pos %d] has [submenu 0x%08lx]\n",mTemp,pos-1,m));
630 //UT_ASSERT(m);
631 pos = 0;
632 break;
633
634 case EV_MLF_EndSubMenu:
635 bResult = stackMenu.pop((void **)&mTemp);
636 UT_ASSERT(bResult);
637 bResult = stackPos.pop((void **)&pos);
638 UT_ASSERT(bResult);
639
640 //UT_DEBUGMSG(("menu::onInitMenu: endSubMenu [popping to menu 0x%08lx pos %d] from 0x%08lx\n",mTemp,pos,m));
641 m = mTemp;
642 break;
643
644 case EV_MLF_BeginPopupMenu:
645 case EV_MLF_EndPopupMenu:
646 break;
647
648 default:
649 UT_ASSERT_HARMLESS(0);
650 break;
651 }
652 }
653
654 #ifdef DEBUG
655 HMENU wDbg = NULL;
656 bResult = stackMenu.pop((void **)&wDbg);
657 UT_ASSERT(bResult);
658 UT_ASSERT(wDbg == hMenuBar);
659 #endif
660
661 return true;
662 }
663
664 /*
665 Caller should DeleteObject the handle returned
666 */
_loadBitmap(XAP_Menu_Id id,int width,int height,const UT_RGBColor & color)667 HBITMAP EV_Win32Menu::_loadBitmap(XAP_Menu_Id id, int width, int height, const UT_RGBColor & color)
668 {
669
670 HBITMAP hBitmap = NULL;
671 EV_Menu_Bitmap* pBitmaps = (EV_Menu_Bitmap*)&s_bitmaps;
672
673 for (; pBitmaps->id;pBitmaps++)
674 {
675 if (pBitmaps->id==id)
676 break;
677 }
678
679 if (!pBitmaps->id)
680 return hBitmap;
681
682 if (!color.isTransparent())
683 XAP_Win32Toolbar_Icons::getBitmapForIcon(GetDesktopWindow(), width, height, &color, pBitmaps->szName, &hBitmap);
684 else
685 XAP_Win32Toolbar_Icons::getAlphaBitmapForIcon(GetDesktopWindow(), width, height, pBitmaps->szName, &hBitmap);
686
687 return hBitmap;
688 }
689
690 // Sets the Bitmap in Windows Vista
_setBitmapforID(HMENU hMenu,XAP_Menu_Id id,UINT cmd)691 void EV_Win32Menu::_setBitmapforID (HMENU hMenu, XAP_Menu_Id id, UINT cmd)
692 {
693 UT_RGBColor Color;
694 Color.m_bIsTransparent=true;
695 HBITMAP hBitmap = EV_Win32Menu::_loadBitmap(id, BITMAP_WITDH, BITMAP_HEIGHT, Color);
696
697 if (hBitmap != NULL) {
698 SetMenuItemBitmaps (hMenu, cmd, MF_BYCOMMAND, hBitmap, hBitmap);
699 m_vechBitmaps.push_back (hBitmap);
700 }
701 }
702
703 /*
704 It tells us which are menu bars
705 */
_isAMenuBar(XAP_Menu_Id id,HMENU hMenu)706 bool EV_Win32Menu::_isAMenuBar(XAP_Menu_Id id, HMENU hMenu)
707 {
708 XAP_Menu_Id ids[] = { AP_MENU_ID_FILE, AP_MENU_ID_EDIT, AP_MENU_ID_VIEW,AP_MENU_ID_INSERT,
709 AP_MENU_ID_TOOLS, AP_MENU_ID_WINDOW, AP_MENU_ID_HELP,AP_MENU_ID_TABLE, AP_MENU_ID_FORMAT, 0 };
710
711 for (UT_uint32 i=0; i<(sizeof(ids)/sizeof(XAP_Menu_Id)); i++)
712 {
713 if (ids[i]==id)
714 {
715
716 MENUITEMINFOW menuInfo;
717 memset (&menuInfo, 0, sizeof(MENUITEMINFOW));
718 menuInfo.cbSize = sizeof(MENUITEMINFOW);
719 menuInfo.fMask = MIIM_DATA;
720 GetMenuItemInfoW(hMenu, 0, TRUE, &menuInfo);
721 EV_Menu_Item* item = (EV_Menu_Item *) menuInfo.dwItemData;
722
723 if (item && id==item->id)
724 {
725 UT_DEBUGMSG(("EV_Win32Menu::_isAMenuBar->%s, 1\n", item->szText));
726 return true;
727 }
728 }
729 }
730
731 UT_DEBUGMSG(("EV_Win32Menu::_isAMenuBar-> 0\n"));
732 return false;
733 }
734
735
736
737 /*
738 Process message WM_MEASUREITEM
739 */
onMeasureItem(HWND hwnd,WPARAM,LPARAM lParam)740 void EV_Win32Menu::onMeasureItem(HWND hwnd, WPARAM /*wParam*/, LPARAM lParam)
741 {
742 SIZE size;
743 LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;
744 HDC hdc = GetDC(hwnd);
745
746 EV_Menu_Item* item = (EV_Menu_Item *) lpmis->itemData;
747 HFONT hfontOld = (HFONT) SelectObject(hdc, m_hFont);
748
749 // Retrieve the width and height of the item's string
750 GetTextExtentPoint32W(hdc, item->szText, lstrlenW(item->szText), &size);
751
752 if (size.cy < item->pMenu->m_nBitmapCY)
753 lpmis->itemHeight = item->pMenu->m_nBitmapCY;
754 else
755 lpmis->itemHeight = size.cy;
756
757 lpmis->itemWidth = size.cx + item->pMenu->m_nBitmapCX + SPACE_ICONTEXT;
758
759 SelectObject(hdc, hfontOld);
760 ReleaseDC(hwnd, hdc);
761 }
762
763 /*
764 Process message WM_MENUCHAR. Process the hotkey messages
765 */
onMenuChar(HWND,WPARAM wParam,LPARAM lParam)766 LPARAM EV_Win32Menu::onMenuChar(HWND /*hwnd*/, WPARAM wParam, LPARAM lParam)
767 {
768 HMENU hMenu = (HMENU) lParam;
769 MENUITEMINFOW menuInfo;
770 int nItems = GetMenuItemCount(hMenu);
771 wchar_t szBuff[1024];
772
773 for (int i=0; i<nItems; i++)
774 {
775 memset (&menuInfo, 0, sizeof(MENUITEMINFOW));
776 menuInfo.cbSize = sizeof(MENUITEMINFOW);
777 menuInfo.fMask = MIIM_DATA;
778
779 GetMenuItemInfoW(hMenu, i, TRUE, &menuInfo);
780
781 EV_Menu_Item* item = (EV_Menu_Item *) menuInfo.dwItemData;
782
783 if (item)
784 {
785 wcscpy (szBuff, item->szText);
786 _wcslwr(szBuff);
787
788 wchar_t* pHotKeyPos = wcschr (szBuff, L'&');
789
790 if (pHotKeyPos)
791 {
792 wchar_t n = (wchar_t)wParam & 0x000000ff;
793
794 pHotKeyPos++;
795
796 if (toupper(*pHotKeyPos)==toupper(n))
797 return MAKELRESULT(i, MNC_EXECUTE);
798
799 }
800 }
801 }
802
803 return MNC_IGNORE;
804 }
805
806 /*
807 Process message WM_DRAWITEM
808 */
onDrawItem(HWND,WPARAM,LPARAM lParam)809 void EV_Win32Menu::onDrawItem(HWND /*hwnd*/, WPARAM /*wParam*/, LPARAM lParam)
810 {
811
812 LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) lParam;
813 EV_Menu_Item* item = (EV_Menu_Item *) lpdis->itemData;
814 COLORREF crText;
815 COLORREF crBkgnd = 0;
816 DWORD dwColor;
817 RECT rect;
818 HBITMAP hBitmap;
819 int colorID;
820 UT_ASSERT(lpdis->CtlType==ODT_MENU);
821 UT_Win32LocaleString sTextRight, sTextLeft;
822
823 /* Rect to draw the text */
824 rect.top = lpdis->rcItem.top;
825 rect.bottom = lpdis->rcItem.bottom;
826
827 if(m_iDIR)
828 {
829 rect.right = lpdis->rcItem.right - 20;
830 rect.left = lpdis->rcItem.left;
831 }
832 else
833 {
834 rect.right = lpdis->rcItem.right;
835 rect.left = /*m_nBitmapCX*/ 20 + lpdis->rcItem.left;
836 }
837
838
839 if (lpdis->itemState & ODS_GRAYED)
840 crText = SetTextColor(lpdis->hDC, GetSysColor(COLOR_GRAYTEXT));
841 else
842 if (lpdis->itemState & ODS_SELECTED)
843 crText = SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
844 else
845 crText = SetTextColor(lpdis->hDC, GetSysColor(COLOR_MENUTEXT));
846
847 if (lpdis->itemState & ODS_SELECTED)
848 colorID = COLOR_HIGHLIGHT;
849 else
850 colorID = COLOR_MENU;
851
852 dwColor = GetSysColor(colorID);
853
854 UT_RGBColor Color(GetRValue(dwColor),GetGValue(dwColor),GetBValue(dwColor));
855 hBitmap = EV_Win32Menu::_loadBitmap(item->id, BITMAP_WITDH, BITMAP_HEIGHT, Color);
856
857 if (hBitmap)
858 {
859 BITMAP bitmap;
860 GetObjectA(hBitmap, sizeof(BITMAP), &bitmap);
861 }
862
863 /*Draw the background of the item*/
864 FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(colorID));
865
866 if(m_iDIR)
867 rect.right = rect.right - SPACE_ICONTEXT;
868 else
869 rect.left = rect.left;
870
871
872 // Select the font associated with the item into the
873 // item's device context, and then draw the string.
874 HFONT hfontOld = (HFONT) SelectObject(lpdis->hDC, item->pMenu->m_hFont);
875
876
877 /*
878 Process tabs
879 */
880 wchar_t* pTabPos = wcschr (item->szText, L'\t');
881
882 if (pTabPos)
883 {
884 wchar_t szTmp[255];
885 wchar_t* pTmp;
886
887 wcsncpy (szTmp, item->szText, pTabPos-item->szText);
888 pTmp = szTmp; pTmp+=pTabPos-item->szText; *pTmp=0;
889 sTextLeft.fromLocale (szTmp);
890
891 wcscpy (szTmp, pTabPos+1);
892 sTextRight.fromLocale (szTmp);
893 sTextRight.appendASCII (" ");
894 }
895 else
896 sTextLeft.fromLocale (item->szText);
897
898 if (lpdis->itemState & ODS_SELECTED)
899 crBkgnd = SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT));
900
901 /* Draw text*/
902 if(m_iDIR)
903 {
904 DrawTextW(lpdis->hDC, sTextLeft.c_str(), sTextLeft.length() , &rect,
905 DT_RIGHT | DT_VCENTER | DT_SINGLELINE | m_iDIR);
906
907 if (sTextRight.length())
908 DrawTextW(lpdis->hDC, sTextRight.c_str(), sTextRight.length(), &rect,
909 DT_LEFT | DT_VCENTER | DT_SINGLELINE | m_iDIR);
910 }
911 else
912 {
913 DrawTextW(lpdis->hDC, sTextLeft.c_str(), sTextLeft.length() , &rect,
914 DT_LEFT | DT_VCENTER | DT_SINGLELINE | m_iDIR);
915
916 if (sTextRight.length())
917 DrawTextW(lpdis->hDC, sTextRight.c_str(), sTextRight.length(), &rect,
918 DT_RIGHT | DT_VCENTER | DT_SINGLELINE | m_iDIR);
919 }
920
921 if (lpdis->itemState & ODS_SELECTED)
922 SetBkColor(lpdis->hDC, crBkgnd);
923
924 /* Draw bitmap*/
925 if (hBitmap)
926 {
927
928 HDC hdcMem = CreateCompatibleDC(lpdis->hDC);
929 SelectObject(hdcMem,(void *)hBitmap);
930
931 if(m_iDIR)
932 BitBlt(lpdis->hDC, lpdis->rcItem.right - 20, lpdis->rcItem.top+1, BITMAP_WITDH, BITMAP_HEIGHT, hdcMem, 0, 0, SRCCOPY );
933 else
934 BitBlt(lpdis->hDC, lpdis->rcItem.left+1, lpdis->rcItem.top+1, BITMAP_WITDH, BITMAP_HEIGHT, hdcMem, 0, 0, SRCCOPY );
935
936 DeleteDC(hdcMem);
937
938 }
939 else
940 if (lpdis->itemState & ODS_CHECKED)
941 {
942 UINT nWidth = GetSystemMetrics(SM_CXMENUCHECK);
943 UINT nHeight = GetSystemMetrics(SM_CYMENUCHECK);
944 RECT r;
945 HBITMAP bm = CreateBitmap(nWidth, nHeight, 1, 1, NULL );
946 HDC hdcMem = CreateCompatibleDC(lpdis->hDC);
947
948 SetBkColor(lpdis->hDC, GetSysColor(colorID));
949 SelectObject(hdcMem, bm);
950 SetRect(&r, 0, 0, nWidth, nHeight);
951 DrawFrameControl(hdcMem, &r, DFC_MENU,DFCS_MENUCHECK);
952
953 if(m_iDIR)
954 BitBlt(lpdis->hDC, lpdis->rcItem.right - 20, lpdis->rcItem.top+2, nWidth, nHeight,
955 hdcMem, 0, 0, SRCCOPY);
956 else
957 BitBlt(lpdis->hDC, lpdis->rcItem.left+2, lpdis->rcItem.top+2, nWidth, nHeight,
958 hdcMem, 0, 0, SRCCOPY);
959
960 DeleteDC(hdcMem);
961 DeleteObject(bm);
962 }
963
964 SelectObject(lpdis->hDC, hfontOld);
965 SetTextColor(lpdis->hDC, crText);
966 if (hBitmap)
967 DeleteObject(hBitmap);
968 }
969
970
onMenuSelect(XAP_Frame * pFrame,AV_View *,HWND,HMENU hMenu,WPARAM wParam)971 bool EV_Win32Menu::onMenuSelect(XAP_Frame * pFrame, AV_View * /*pView*/,
972 HWND /*hWnd*/, HMENU hMenu, WPARAM wParam)
973 {
974 UINT nItemID = (UINT)LOWORD(wParam);
975 UINT nFlags = (UINT)HIWORD(wParam);
976
977 if ( (nFlags==0xffff) && (hMenu==0) )
978 {
979 //UT_DEBUGMSG(("ClearMessage 1\n"));
980 pFrame->setStatusMessage(NULL);
981 return true;
982 }
983
984 if ( (nItemID==0) || (nFlags & (MF_SEPARATOR|MF_POPUP)) )
985 {
986 //UT_DEBUGMSG(("ClearMessage 2\n"));
987 pFrame->setStatusMessage(NULL);
988 return true;
989 }
990
991 if (nFlags & (MF_SYSMENU))
992 {
993 //UT_DEBUGMSG(("SysMenu [%x]\n",nItemID));
994 pFrame->setStatusMessage(NULL);
995 return true;
996 }
997
998 XAP_Menu_Id id = MenuIdFromWmCommand(nItemID);
999 EV_Menu_Label * pLabel = m_pMenuLabelSet->getLabel(id);
1000 if (!pLabel)
1001 {
1002 //UT_DEBUGMSG(("ClearMessage 3 [%d %d]\n",nItemID,id));
1003 pFrame->setStatusMessage(NULL);
1004 return true;
1005 }
1006
1007 const char * szMsg = pLabel->getMenuStatusMessage();
1008 if (!szMsg || !*szMsg)
1009 szMsg = "TODO This menu item doesn't have a StatusMessage defined.";
1010
1011 pFrame->setStatusMessage(szMsg);
1012 return true;
1013 }
1014
1015
1016
1017 /*****************************************************************/
1018 /*****************************************************************/
1019
EV_Win32MenuBar(XAP_Win32App * pWin32App,const EV_EditEventMapper * pEEM,const char * szMenuLayoutName,const char * szMenuLabelSetName)1020 EV_Win32MenuBar::EV_Win32MenuBar(XAP_Win32App * pWin32App,
1021 const EV_EditEventMapper * pEEM,
1022 const char * szMenuLayoutName,
1023 const char * szMenuLabelSetName)
1024 : EV_Win32Menu(pWin32App,pEEM,szMenuLayoutName,szMenuLabelSetName)
1025 {
1026 }
1027
~EV_Win32MenuBar(void)1028 EV_Win32MenuBar::~EV_Win32MenuBar(void)
1029 {
1030 destroy();
1031 // TODO should we destroy m_myMenu if set ??
1032 }
1033
synthesizeMenuBar(XAP_Frame * pFrame)1034 bool EV_Win32MenuBar::synthesizeMenuBar(XAP_Frame * pFrame)
1035 {
1036 m_myMenu = CreateMenu();
1037
1038 bool bRet = synthesizeMenu(pFrame, m_myMenu);
1039
1040 // when dealing with RTL interface language, we need to tell do some tricks here to
1041 // make the menubar to layout RTL
1042 // see http://www.microsoft.com/middleeast/msdn/faq.aspx
1043 MENUITEMINFOW mii;
1044
1045 wchar_t buff[81];
1046 memset(buff, 0, 80 * sizeof (wchar_t));
1047 buff[80] = 0;
1048
1049 mii.cbSize = sizeof(mii);
1050 mii.dwTypeData = buff;
1051 mii.fType = MF_STRING;
1052 mii.cch = 80* sizeof (wchar_t);
1053 mii.fState = MFS_DEFAULT;
1054 mii.fMask = MIIM_ID | MIIM_DATA | MIIM_TYPE | MIIM_SUBMENU;
1055
1056 if(m_iDIR && GetMenuItemInfoW(m_myMenu, 0, 1, &mii))
1057 {
1058 mii.fType |= MFT_RIGHTORDER;
1059 SetMenuItemInfoW(m_myMenu, 0, 1, &mii);
1060 }
1061
1062 return bRet;
1063 }
1064
1065 /*****************************************************************/
1066 /*****************************************************************/
1067
EV_Win32MenuPopup(XAP_Win32App * pWin32App,const char * szMenuLayoutName,const char * szMenuLabelSetName)1068 EV_Win32MenuPopup::EV_Win32MenuPopup(XAP_Win32App * pWin32App,
1069 const char * szMenuLayoutName,
1070 const char * szMenuLabelSetName)
1071 : EV_Win32Menu(pWin32App,NULL,szMenuLayoutName,szMenuLabelSetName)
1072 {
1073 }
1074
~EV_Win32MenuPopup(void)1075 EV_Win32MenuPopup::~EV_Win32MenuPopup(void)
1076 {
1077 destroy();
1078
1079 if (m_myMenu)
1080 DestroyMenu(m_myMenu);
1081 }
1082
synthesizeMenuPopup(XAP_Frame * pFrame)1083 bool EV_Win32MenuPopup::synthesizeMenuPopup(XAP_Frame * pFrame)
1084 {
1085 m_myMenu = CreatePopupMenu();
1086
1087 bool bRet = synthesizeMenu(pFrame, m_myMenu);
1088
1089 // when dealing with RTL interface language, we need to tell do some tricks here to
1090 // make the menubar to layout RTL
1091 // see http://www.microsoft.com/middleeast/msdn/faq.aspx
1092 MENUITEMINFOW mii;
1093 wchar_t buff[81];
1094 memset(buff, 80 * sizeof (wchar_t), L' ');
1095 buff[80] = 0;
1096
1097 mii.cbSize = sizeof(mii);
1098 mii.dwTypeData = buff;
1099 mii.fType = MF_STRING;
1100 mii.cch = 80* sizeof (wchar_t);
1101 mii.fState = MFS_DEFAULT;
1102 mii.fMask = MIIM_ID | MIIM_DATA | MIIM_TYPE | MIIM_SUBMENU;
1103
1104 if(m_iDIR && GetMenuItemInfoW(m_myMenu, 0, 1, &mii))
1105 {
1106 mii.fType |= MFT_RIGHTORDER;
1107 SetMenuItemInfoW(m_myMenu, 0, 1, &mii);
1108 }
1109
1110 return bRet;
1111
1112 }
1113
1114