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