Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #include <unx/gtk/gtksalmenu.hxx>
11 :
12 : #ifdef ENABLE_GMENU_INTEGRATION
13 :
14 : #include <generic/gendata.hxx>
15 : #include <unx/saldisp.hxx>
16 : #include <unx/gtk/glomenu.h>
17 : #include <unx/gtk/gloactiongroup.h>
18 : #include <vcl/menu.hxx>
19 : #include <unx/gtk/gtkinst.hxx>
20 :
21 : #if GTK_CHECK_VERSION(3,0,0)
22 : # include <gdk/gdkkeysyms-compat.h>
23 : #endif
24 :
25 : #include <sal/log.hxx>
26 :
27 : // FIXME Copied from framework/inc/framework/menuconfiguration.hxx to
28 : // avoid circular dependency between modules. It should be in a common
29 : // header (probably in vcl).
30 : const sal_uInt16 START_ITEMID_WINDOWLIST = 4600;
31 : const sal_uInt16 END_ITEMID_WINDOWLIST = 4699;
32 :
33 : static bool bMenuVisibility = false;
34 :
35 : /*
36 : * This function generates the proper command name for all actions, including
37 : * duplicated or special ones.
38 : */
39 0 : static gchar* GetCommandForItem( GtkSalMenuItem* pSalMenuItem, gchar* aCurrentCommand, GActionGroup* pActionGroup )
40 : {
41 0 : gchar* aCommand = NULL;
42 :
43 0 : sal_uInt16 nId = pSalMenuItem->mnId;
44 0 : Menu* pMenu = pSalMenuItem->mpVCLMenu;
45 :
46 : // If item belongs to window list, generate a command with "window-(id)" format.
47 0 : if ( ( nId >= START_ITEMID_WINDOWLIST ) && ( nId <= END_ITEMID_WINDOWLIST ) )
48 0 : aCommand = g_strdup_printf( "window-%d", nId );
49 : else
50 : {
51 0 : if ( !pMenu )
52 0 : return NULL;
53 :
54 0 : OUString aMenuCommand = pMenu->GetItemCommand( nId );
55 0 : gchar* aCommandStr = g_strdup( OUStringToOString( aMenuCommand, RTL_TEXTENCODING_UTF8 ).getStr() );
56 0 : aCommand = g_strdup( aCommandStr );
57 :
58 : // Some items could have duplicated commands. A new one should be generated.
59 0 : for ( sal_uInt16 i = 2; ; i++ )
60 : {
61 0 : if ( !g_action_group_has_action( pActionGroup, aCommand )
62 0 : || ( aCurrentCommand && g_strcmp0( aCurrentCommand, aCommand ) == 0 ) )
63 0 : break;
64 :
65 0 : g_free( aCommand );
66 0 : aCommand = g_strdup_printf("%s%d", aCommandStr, i);
67 0 : }
68 :
69 0 : g_free( aCommandStr );
70 : }
71 :
72 0 : return aCommand;
73 : }
74 :
75 0 : static void KeyCodeToGdkKey ( const vcl::KeyCode& rKeyCode, guint* pGdkKeyCode, GdkModifierType *pGdkModifiers )
76 : {
77 0 : if ( pGdkKeyCode == NULL || pGdkModifiers == NULL )
78 0 : return;
79 :
80 : // Get GDK key modifiers
81 0 : GdkModifierType nModifiers = (GdkModifierType) 0;
82 :
83 0 : if ( rKeyCode.IsShift() )
84 0 : nModifiers = (GdkModifierType) ( nModifiers | GDK_SHIFT_MASK );
85 :
86 0 : if ( rKeyCode.IsMod1() )
87 0 : nModifiers = (GdkModifierType) ( nModifiers | GDK_CONTROL_MASK );
88 :
89 0 : if ( rKeyCode.IsMod2() )
90 0 : nModifiers = (GdkModifierType) ( nModifiers | GDK_MOD1_MASK );
91 :
92 0 : *pGdkModifiers = nModifiers;
93 :
94 : // Get GDK keycode.
95 0 : guint nKeyCode = 0;
96 :
97 0 : guint nCode = rKeyCode.GetCode();
98 :
99 0 : if ( nCode >= KEY_0 && nCode <= KEY_9 )
100 0 : nKeyCode = ( nCode - KEY_0 ) + GDK_0;
101 0 : else if ( nCode >= KEY_A && nCode <= KEY_Z )
102 0 : nKeyCode = ( nCode - KEY_A ) + GDK_A;
103 0 : else if ( nCode >= KEY_F1 && nCode <= KEY_F26 )
104 0 : nKeyCode = ( nCode - KEY_F1 ) + GDK_F1;
105 : else
106 : {
107 0 : switch( nCode )
108 : {
109 0 : case KEY_DOWN: nKeyCode = GDK_Down; break;
110 0 : case KEY_UP: nKeyCode = GDK_Up; break;
111 0 : case KEY_LEFT: nKeyCode = GDK_Left; break;
112 0 : case KEY_RIGHT: nKeyCode = GDK_Right; break;
113 0 : case KEY_HOME: nKeyCode = GDK_Home; break;
114 0 : case KEY_END: nKeyCode = GDK_End; break;
115 0 : case KEY_PAGEUP: nKeyCode = GDK_Page_Up; break;
116 0 : case KEY_PAGEDOWN: nKeyCode = GDK_Page_Down; break;
117 0 : case KEY_RETURN: nKeyCode = GDK_Return; break;
118 0 : case KEY_ESCAPE: nKeyCode = GDK_Escape; break;
119 0 : case KEY_TAB: nKeyCode = GDK_Tab; break;
120 0 : case KEY_BACKSPACE: nKeyCode = GDK_BackSpace; break;
121 0 : case KEY_SPACE: nKeyCode = GDK_space; break;
122 0 : case KEY_INSERT: nKeyCode = GDK_Insert; break;
123 0 : case KEY_DELETE: nKeyCode = GDK_Delete; break;
124 0 : case KEY_ADD: nKeyCode = GDK_plus; break;
125 0 : case KEY_SUBTRACT: nKeyCode = GDK_minus; break;
126 0 : case KEY_MULTIPLY: nKeyCode = GDK_asterisk; break;
127 0 : case KEY_DIVIDE: nKeyCode = GDK_slash; break;
128 0 : case KEY_POINT: nKeyCode = GDK_period; break;
129 0 : case KEY_COMMA: nKeyCode = GDK_comma; break;
130 0 : case KEY_LESS: nKeyCode = GDK_less; break;
131 0 : case KEY_GREATER: nKeyCode = GDK_greater; break;
132 0 : case KEY_EQUAL: nKeyCode = GDK_equal; break;
133 0 : case KEY_FIND: nKeyCode = GDK_Find; break;
134 0 : case KEY_CONTEXTMENU: nKeyCode = GDK_Menu; break;
135 0 : case KEY_HELP: nKeyCode = GDK_Help; break;
136 0 : case KEY_UNDO: nKeyCode = GDK_Undo; break;
137 0 : case KEY_REPEAT: nKeyCode = GDK_Redo; break;
138 0 : case KEY_DECIMAL: nKeyCode = GDK_KP_Decimal; break;
139 0 : case KEY_TILDE: nKeyCode = GDK_asciitilde; break;
140 0 : case KEY_QUOTELEFT: nKeyCode = GDK_quoteleft; break;
141 0 : case KEY_BRACKETLEFT: nKeyCode = GDK_bracketleft; break;
142 0 : case KEY_BRACKETRIGHT: nKeyCode = GDK_bracketright; break;
143 0 : case KEY_SEMICOLON: nKeyCode = GDK_semicolon; break;
144 0 : case KEY_QUOTERIGHT: nKeyCode = GDK_quoteright; break;
145 :
146 : // Special cases
147 0 : case KEY_COPY: nKeyCode = GDK_Copy; break;
148 0 : case KEY_CUT: nKeyCode = GDK_Cut; break;
149 0 : case KEY_PASTE: nKeyCode = GDK_Paste; break;
150 0 : case KEY_OPEN: nKeyCode = GDK_Open; break;
151 : }
152 : }
153 :
154 0 : *pGdkKeyCode = nKeyCode;
155 : }
156 :
157 0 : bool GtkSalMenu::PrepUpdate()
158 : {
159 0 : const GtkSalFrame* pFrame = GetFrame();
160 0 : if (pFrame)
161 : {
162 0 : GtkSalFrame* pNonConstFrame = ( GtkSalFrame* ) pFrame;
163 0 : GtkSalMenu* pSalMenu = ((GtkSalMenu*) this);
164 :
165 0 : if ( !pNonConstFrame->GetMenu() )
166 0 : pNonConstFrame->SetMenu( pSalMenu );
167 :
168 0 : if ( bMenuVisibility && mpMenuModel && mpActionGroup )
169 0 : return true;
170 : }
171 :
172 0 : return false;
173 : }
174 :
175 : /*
176 : * Menu updating methods
177 : */
178 :
179 0 : void RemoveSpareItemsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, unsigned nSection, unsigned nValidItems )
180 : {
181 0 : sal_Int32 nSectionItems = g_lo_menu_get_n_items_from_section( pMenu, nSection );
182 :
183 0 : while ( nSectionItems > (sal_Int32) nValidItems )
184 : {
185 0 : gchar* aCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, --nSectionItems );
186 :
187 0 : if ( aCommand != NULL && pOldCommandList != NULL )
188 0 : *pOldCommandList = g_list_append( *pOldCommandList, g_strdup( aCommand ) );
189 :
190 0 : g_free( aCommand );
191 :
192 0 : g_lo_menu_remove_from_section( pMenu, nSection, nSectionItems );
193 : }
194 0 : }
195 :
196 0 : void RemoveSpareSectionsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, unsigned nLastSection )
197 : {
198 0 : if ( pMenu == NULL || pOldCommandList == NULL )
199 0 : return;
200 :
201 0 : sal_Int32 n = g_menu_model_get_n_items( G_MENU_MODEL( pMenu ) ) - 1;
202 :
203 0 : for ( ; n > (sal_Int32) nLastSection; n-- )
204 : {
205 0 : RemoveSpareItemsFromNativeMenu( pMenu, pOldCommandList, n, 0 );
206 0 : g_lo_menu_remove( pMenu, n );
207 : }
208 : }
209 :
210 0 : gint CompareStr( gpointer str1, gpointer str2 )
211 : {
212 0 : return g_strcmp0( (const gchar*) str1, (const gchar*) str2 );
213 : }
214 :
215 0 : void RemoveUnusedCommands( GLOActionGroup* pActionGroup, GList* pOldCommandList, GList* pNewCommandList )
216 : {
217 0 : if ( pActionGroup == NULL || pOldCommandList == NULL )
218 : {
219 0 : g_list_free_full( pOldCommandList, g_free );
220 0 : g_list_free_full( pNewCommandList, g_free );
221 0 : return;
222 : }
223 :
224 0 : while ( pNewCommandList != NULL )
225 : {
226 0 : GList* pNewCommand = g_list_first( pNewCommandList );
227 0 : pNewCommandList = g_list_remove_link( pNewCommandList, pNewCommand );
228 :
229 0 : gpointer aCommand = g_list_nth_data( pNewCommand, 0 );
230 :
231 0 : GList* pOldCommand = g_list_find_custom( pOldCommandList, aCommand, (GCompareFunc) CompareStr );
232 :
233 0 : if ( pOldCommand != NULL )
234 : {
235 0 : pOldCommandList = g_list_remove_link( pOldCommandList, pOldCommand );
236 0 : g_list_free_full( pOldCommand, g_free );
237 : }
238 :
239 0 : g_list_free_full( pNewCommand, g_free );
240 : }
241 :
242 0 : while ( pOldCommandList != NULL )
243 : {
244 0 : GList* pCommand = g_list_first( pOldCommandList );
245 0 : pOldCommandList = g_list_remove_link( pOldCommandList, pCommand );
246 :
247 0 : gchar* aCommand = (gchar*) g_list_nth_data( pCommand, 0 );
248 :
249 0 : g_lo_action_group_remove( pActionGroup, aCommand );
250 :
251 0 : g_list_free_full( pCommand, g_free );
252 : }
253 : }
254 :
255 0 : void GtkSalMenu::ImplUpdate( gboolean bRecurse )
256 : {
257 0 : SolarMutexGuard aGuard;
258 :
259 : SAL_INFO("vcl.unity", "ImplUpdate pre PrepUpdate");
260 0 : if( !PrepUpdate() )
261 0 : return;
262 :
263 0 : Menu* pVCLMenu = mpVCLMenu;
264 0 : GLOMenu* pLOMenu = G_LO_MENU( mpMenuModel );
265 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
266 : SAL_INFO("vcl.unity", "Syncing vcl menu " << pVCLMenu << " to menu model " << pLOMenu << " and action group " << pActionGroup);
267 0 : GList *pOldCommandList = NULL;
268 0 : GList *pNewCommandList = NULL;
269 :
270 0 : sal_uInt16 nLOMenuSize = g_menu_model_get_n_items( G_MENU_MODEL( pLOMenu ) );
271 :
272 0 : if ( nLOMenuSize == 0 )
273 0 : g_lo_menu_new_section( pLOMenu, 0, NULL );
274 :
275 0 : sal_Int32 nSection = 0;
276 0 : sal_Int32 nItemPos = 0;
277 0 : sal_Int32 validItems = 0;
278 : sal_Int32 nItem;
279 :
280 0 : for ( nItem = 0; nItem < ( sal_Int32 ) GetItemCount(); nItem++ ) {
281 0 : if ( !IsItemVisible( nItem ) )
282 0 : continue;
283 :
284 0 : GtkSalMenuItem *pSalMenuItem = GetItemAtPos( nItem );
285 0 : sal_uInt16 nId = pSalMenuItem->mnId;
286 :
287 0 : if ( pSalMenuItem->mnType == MenuItemType::SEPARATOR )
288 : {
289 : // Delete extra items from current section.
290 0 : RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems );
291 :
292 0 : nSection++;
293 0 : nItemPos = 0;
294 0 : validItems = 0;
295 :
296 0 : if ( nLOMenuSize <= nSection )
297 : {
298 0 : g_lo_menu_new_section( pLOMenu, nSection, NULL );
299 0 : nLOMenuSize++;
300 : }
301 :
302 0 : continue;
303 : }
304 :
305 0 : if ( nItemPos >= g_lo_menu_get_n_items_from_section( pLOMenu, nSection ) )
306 0 : g_lo_menu_insert_in_section( pLOMenu, nSection, nItemPos, "EMPTY STRING" );
307 :
308 : // Get internal menu item values.
309 0 : OUString aText = pVCLMenu->GetItemText( nId );
310 0 : bool bEnabled = pVCLMenu->IsItemEnabled( nId );
311 0 : vcl::KeyCode nAccelKey = pVCLMenu->GetAccelKey( nId );
312 0 : bool bChecked = pVCLMenu->IsItemChecked( nId );
313 0 : MenuItemBits itemBits = pVCLMenu->GetItemBits( nId );
314 :
315 : // Store current item command in command list.
316 0 : gchar *aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pLOMenu, nSection, nItemPos );
317 :
318 0 : if ( aCurrentCommand != NULL )
319 0 : pOldCommandList = g_list_append( pOldCommandList, aCurrentCommand );
320 :
321 : // Get the new command for the item.
322 0 : gchar* aNativeCommand = GetCommandForItem( pSalMenuItem, aCurrentCommand, mpActionGroup );
323 :
324 : // Force updating of native menu labels.
325 0 : NativeSetItemText( nSection, nItemPos, aText );
326 0 : NativeSetAccelerator( nSection, nItemPos, nAccelKey, nAccelKey.GetName( GetFrame()->GetWindow() ) );
327 :
328 0 : if ( g_strcmp0( aNativeCommand, "" ) != 0 && pSalMenuItem->mpSubMenu == NULL )
329 : {
330 0 : NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, bChecked, FALSE );
331 0 : NativeCheckItem( nSection, nItemPos, itemBits, bChecked );
332 0 : NativeSetEnableItem( aNativeCommand, bEnabled );
333 :
334 0 : pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) );
335 : }
336 :
337 0 : GtkSalMenu* pSubmenu = pSalMenuItem->mpSubMenu;
338 :
339 0 : if ( pSubmenu && pSubmenu->GetMenu() )
340 : {
341 0 : NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, FALSE, TRUE );
342 0 : pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) );
343 :
344 0 : GLOMenu* pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos );
345 :
346 0 : if ( pSubMenuModel == NULL )
347 : {
348 0 : g_lo_menu_new_submenu_in_item_in_section( pLOMenu, nSection, nItemPos );
349 0 : pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos );
350 : }
351 :
352 0 : g_object_unref( pSubMenuModel );
353 :
354 0 : if ( bRecurse )
355 : {
356 : SAL_INFO("vcl.unity", "preparing submenu " << pSubMenuModel << " to menu model " << G_MENU_MODEL(pSubMenuModel) << " and action group " << G_ACTION_GROUP(pActionGroup));
357 0 : pSubmenu->SetMenuModel( G_MENU_MODEL( pSubMenuModel ) );
358 0 : pSubmenu->SetActionGroup( G_ACTION_GROUP( pActionGroup ) );
359 0 : pSubmenu->ImplUpdate( bRecurse );
360 : }
361 : }
362 :
363 0 : g_free( aNativeCommand );
364 :
365 0 : ++nItemPos;
366 0 : ++validItems;
367 0 : }
368 :
369 : // Delete extra items in last section.
370 0 : RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems );
371 :
372 : // Delete extra sections.
373 0 : RemoveSpareSectionsFromNativeMenu( pLOMenu, &pOldCommandList, nSection );
374 :
375 : // Delete unused commands.
376 0 : RemoveUnusedCommands( pActionGroup, pOldCommandList, pNewCommandList );
377 : }
378 :
379 0 : void GtkSalMenu::Update()
380 : {
381 0 : ImplUpdate( FALSE );
382 0 : }
383 :
384 0 : void GtkSalMenu::UpdateFull()
385 : {
386 0 : ImplUpdate( TRUE );
387 0 : }
388 :
389 : /*
390 : * GtkSalMenu
391 : */
392 :
393 0 : GtkSalMenu::GtkSalMenu( bool bMenuBar ) :
394 : mbMenuBar( bMenuBar ),
395 : mpVCLMenu( NULL ),
396 : mpOldSalMenu( NULL ),
397 : mpParentSalMenu( NULL ),
398 : mpFrame( NULL ),
399 : mpMenuModel( NULL ),
400 0 : mpActionGroup( NULL )
401 : {
402 0 : }
403 :
404 0 : GtkSalMenu::~GtkSalMenu()
405 : {
406 0 : SolarMutexGuard aGuard;
407 :
408 0 : if ( mbMenuBar )
409 : {
410 0 : if ( mpMenuModel )
411 : {
412 : // g_lo_menu_remove( G_LO_MENU( mpMenuModel ), 0 );
413 0 : g_object_unref( mpMenuModel );
414 : }
415 : }
416 :
417 0 : maItems.clear();
418 0 : }
419 :
420 0 : bool GtkSalMenu::VisibleMenuBar()
421 : {
422 0 : return bMenuVisibility;
423 : }
424 :
425 0 : void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
426 : {
427 0 : SolarMutexGuard aGuard;
428 0 : GtkSalMenuItem *pItem = static_cast<GtkSalMenuItem*>( pSalMenuItem );
429 :
430 0 : if ( nPos == MENU_APPEND )
431 0 : maItems.push_back( pItem );
432 : else
433 0 : maItems.insert( maItems.begin() + nPos, pItem );
434 :
435 0 : pItem->mpParentMenu = this;
436 0 : }
437 :
438 0 : void GtkSalMenu::RemoveItem( unsigned nPos )
439 : {
440 0 : SolarMutexGuard aGuard;
441 0 : maItems.erase( maItems.begin() + nPos );
442 0 : }
443 :
444 0 : void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned )
445 : {
446 0 : SolarMutexGuard aGuard;
447 0 : GtkSalMenuItem *pItem = static_cast< GtkSalMenuItem* >( pSalMenuItem );
448 0 : GtkSalMenu *pGtkSubMenu = static_cast< GtkSalMenu* >( pSubMenu );
449 :
450 0 : if ( pGtkSubMenu == NULL )
451 0 : return;
452 :
453 0 : pGtkSubMenu->mpParentSalMenu = this;
454 0 : pItem->mpSubMenu = pGtkSubMenu;
455 : }
456 :
457 : static bool bInvalidMenus = false;
458 0 : static gboolean RefreshMenusUnity(gpointer)
459 : {
460 0 : SolarMutexGuard g;
461 :
462 0 : SalDisplay* pSalDisplay = GetGenericData()->GetSalDisplay();
463 0 : std::list< SalFrame* >::const_iterator pSalFrame = pSalDisplay->getFrames().begin();
464 0 : std::list< SalFrame* >::const_iterator pEndSalFrame = pSalDisplay->getFrames().end();
465 0 : for(; pSalFrame != pEndSalFrame; ++pSalFrame) {
466 0 : const GtkSalFrame* pGtkSalFrame = static_cast< const GtkSalFrame* >( *pSalFrame );
467 0 : GtkSalFrame* pFrameNonConst = const_cast<GtkSalFrame*>(pGtkSalFrame);
468 0 : GtkSalMenu* pSalMenu = static_cast<GtkSalMenu*>(pFrameNonConst->GetMenu());
469 0 : if(pSalMenu) {
470 0 : pSalMenu->Activate();
471 0 : pSalMenu->UpdateFull();
472 : }
473 : }
474 0 : bInvalidMenus = false;
475 0 : return FALSE;
476 : }
477 :
478 0 : static long RefreshMenusUnity(void*, void*)
479 : {
480 0 : if(!bInvalidMenus) {
481 0 : g_timeout_add(10, &RefreshMenusUnity, NULL);
482 0 : bInvalidMenus = true;
483 : }
484 0 : return 0;
485 : }
486 :
487 0 : static Link* getRefreshLinkInstance()
488 : {
489 : static Link* pLink = NULL;
490 0 : if(!pLink) {
491 0 : pLink = new Link(NULL, &RefreshMenusUnity);
492 : }
493 0 : return pLink;
494 : }
495 :
496 0 : void GtkSalMenu::SetFrame( const SalFrame* pFrame )
497 : {
498 0 : SolarMutexGuard aGuard;
499 : {
500 0 : vcl::MenuInvalidator aInvalidator;
501 0 : aInvalidator.GetMenuInvalidateListeners()->addListener(*getRefreshLinkInstance());
502 : }
503 :
504 : assert(mbMenuBar);
505 : SAL_INFO("vcl.unity", "GtkSalMenu set to frame");
506 0 : mpFrame = static_cast< const GtkSalFrame* >( pFrame );
507 0 : GtkSalFrame* pFrameNonConst = const_cast<GtkSalFrame*>(mpFrame);
508 :
509 : // if we had a menu on the GtkSalMenu we have to free it as we generate a
510 : // full menu anyway and we might need to reuse an existing model and
511 : // actiongroup
512 0 : mpOldSalMenu = static_cast< GtkSalMenu* >( pFrameNonConst->GetMenu() );
513 0 : pFrameNonConst->SetMenu( this );
514 0 : pFrameNonConst->EnsureAppMenuWatch();
515 :
516 : // Clean menu model and action group if needed.
517 0 : GtkWidget* pWidget = pFrameNonConst->getWindow();
518 0 : GdkWindow* gdkWindow = gtk_widget_get_window( pWidget );
519 :
520 0 : GLOMenu* pMenuModel = G_LO_MENU( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) );
521 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-action-group" ) );
522 : SAL_INFO("vcl.unity", "Found menu model: " << pMenuModel << " and action group: " << pActionGroup);
523 :
524 0 : if ( pMenuModel )
525 : {
526 0 : if ( g_menu_model_get_n_items( G_MENU_MODEL( pMenuModel ) ) > 0 )
527 0 : g_lo_menu_remove( pMenuModel, 0 );
528 :
529 0 : mpMenuModel = G_MENU_MODEL( g_lo_menu_new() );
530 : }
531 :
532 0 : if ( pActionGroup )
533 : {
534 0 : g_lo_action_group_clear( pActionGroup );
535 0 : mpActionGroup = G_ACTION_GROUP( pActionGroup );
536 : }
537 :
538 : // Generate the main menu structure.
539 0 : if (bMenuVisibility)
540 0 : UpdateFull();
541 :
542 0 : g_lo_menu_insert_section( pMenuModel, 0, NULL, mpMenuModel );
543 0 : }
544 :
545 0 : const GtkSalFrame* GtkSalMenu::GetFrame() const
546 : {
547 0 : SolarMutexGuard aGuard;
548 0 : const GtkSalMenu* pMenu = this;
549 0 : while( pMenu && ! pMenu->mpFrame )
550 0 : pMenu = pMenu->mpParentSalMenu;
551 0 : return pMenu ? pMenu->mpFrame : NULL;
552 : }
553 :
554 0 : void GtkSalMenu::NativeCheckItem( unsigned nSection, unsigned nItemPos, MenuItemBits bits, gboolean bCheck )
555 : {
556 0 : SolarMutexGuard aGuard;
557 :
558 0 : if ( mpActionGroup == NULL )
559 0 : return;
560 :
561 0 : gchar* aCommand = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
562 :
563 0 : if ( aCommand != NULL || g_strcmp0( aCommand, "" ) != 0 )
564 : {
565 0 : GVariant *pCheckValue = NULL;
566 0 : GVariant *pCurrentState = g_action_group_get_action_state( mpActionGroup, aCommand );
567 :
568 0 : if ( bits & MenuItemBits::RADIOCHECK )
569 0 : pCheckValue = bCheck ? g_variant_new_string( aCommand ) : g_variant_new_string( "" );
570 : else
571 : {
572 : // By default, all checked items are checkmark buttons.
573 0 : if ( bCheck || ( !bCheck && pCurrentState != NULL ) )
574 0 : pCheckValue = g_variant_new_boolean( bCheck );
575 : }
576 :
577 0 : if ( pCheckValue != NULL )
578 : {
579 0 : if ( pCurrentState == NULL || g_variant_equal( pCurrentState, pCheckValue ) == FALSE )
580 : {
581 0 : g_action_group_change_action_state( mpActionGroup, aCommand, pCheckValue );
582 : }
583 : else
584 : {
585 0 : g_variant_unref (pCheckValue);
586 : }
587 : }
588 :
589 0 : if ( pCurrentState != NULL )
590 0 : g_variant_unref( pCurrentState );
591 : }
592 :
593 0 : if ( aCommand )
594 0 : g_free( aCommand );
595 : }
596 :
597 0 : void GtkSalMenu::NativeSetEnableItem( gchar* aCommand, gboolean bEnable )
598 : {
599 0 : SolarMutexGuard aGuard;
600 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
601 :
602 0 : if ( g_action_group_get_action_enabled( G_ACTION_GROUP( pActionGroup ), aCommand ) != bEnable )
603 0 : g_lo_action_group_set_action_enabled( pActionGroup, aCommand, bEnable );
604 0 : }
605 :
606 0 : void GtkSalMenu::NativeSetItemText( unsigned nSection, unsigned nItemPos, const OUString& rText )
607 : {
608 0 : SolarMutexGuard aGuard;
609 : // Escape all underscores so that they don't get interpreted as hotkeys
610 0 : OUString aText = rText.replaceAll( "_", "__" );
611 : // Replace the LibreOffice hotkey identifier with an underscore
612 0 : aText = aText.replace( '~', '_' );
613 0 : OString aConvertedText = OUStringToOString( aText, RTL_TEXTENCODING_UTF8 );
614 :
615 : // Update item text only when necessary.
616 0 : gchar* aLabel = g_lo_menu_get_label_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
617 :
618 0 : if ( aLabel == NULL || g_strcmp0( aLabel, aConvertedText.getStr() ) != 0 )
619 0 : g_lo_menu_set_label_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aConvertedText.getStr() );
620 :
621 0 : if ( aLabel )
622 0 : g_free( aLabel );
623 0 : }
624 :
625 0 : void GtkSalMenu::NativeSetAccelerator( unsigned nSection, unsigned nItemPos, const vcl::KeyCode& rKeyCode, const OUString& rKeyName )
626 : {
627 0 : SolarMutexGuard aGuard;
628 :
629 0 : if ( rKeyName.isEmpty() )
630 0 : return;
631 :
632 : guint nKeyCode;
633 : GdkModifierType nModifiers;
634 :
635 0 : KeyCodeToGdkKey( rKeyCode, &nKeyCode, &nModifiers );
636 :
637 0 : gchar* aAccelerator = gtk_accelerator_name( nKeyCode, nModifiers );
638 :
639 0 : gchar* aCurrentAccel = g_lo_menu_get_accelerator_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
640 :
641 0 : if ( aCurrentAccel == NULL && g_strcmp0( aCurrentAccel, aAccelerator ) != 0 )
642 0 : g_lo_menu_set_accelerator_to_item_in_section ( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aAccelerator );
643 :
644 0 : g_free( aAccelerator );
645 0 : g_free( aCurrentAccel );
646 : }
647 :
648 0 : void GtkSalMenu::NativeSetItemCommand( unsigned nSection,
649 : unsigned nItemPos,
650 : sal_uInt16 nId,
651 : const gchar* aCommand,
652 : MenuItemBits nBits,
653 : gboolean bChecked,
654 : gboolean bIsSubmenu )
655 : {
656 0 : SolarMutexGuard aGuard;
657 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
658 :
659 0 : GVariant *pTarget = NULL;
660 :
661 0 : if ( g_action_group_has_action( mpActionGroup, aCommand ) == FALSE ) {
662 0 : if ( ( nBits & MenuItemBits::CHECKABLE ) || bIsSubmenu )
663 : {
664 : // Item is a checkmark button.
665 0 : GVariantType* pStateType = g_variant_type_new( (gchar*) G_VARIANT_TYPE_BOOLEAN );
666 0 : GVariant* pState = g_variant_new_boolean( bChecked );
667 :
668 0 : g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, bIsSubmenu, NULL, pStateType, NULL, pState );
669 : }
670 0 : else if ( nBits & MenuItemBits::RADIOCHECK )
671 : {
672 : // Item is a radio button.
673 0 : GVariantType* pParameterType = g_variant_type_new( (gchar*) G_VARIANT_TYPE_STRING );
674 0 : GVariantType* pStateType = g_variant_type_new( (gchar*) G_VARIANT_TYPE_STRING );
675 0 : GVariant* pState = g_variant_new_string( "" );
676 0 : pTarget = g_variant_new_string( aCommand );
677 :
678 0 : g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, FALSE, pParameterType, pStateType, NULL, pState );
679 : }
680 : else
681 : {
682 : // Item is not special, so insert a stateless action.
683 0 : g_lo_action_group_insert( pActionGroup, aCommand, nId, FALSE );
684 : }
685 : }
686 :
687 0 : GLOMenu* pMenu = G_LO_MENU( mpMenuModel );
688 :
689 : // Menu item is not updated unless it's necessary.
690 0 : gchar* aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, nItemPos );
691 :
692 0 : if ( aCurrentCommand == NULL || g_strcmp0( aCurrentCommand, aCommand ) != 0 )
693 : {
694 0 : g_lo_menu_set_command_to_item_in_section( pMenu, nSection, nItemPos, aCommand );
695 :
696 0 : gchar* aItemCommand = g_strconcat("win.", aCommand, NULL );
697 :
698 0 : if ( bIsSubmenu )
699 0 : g_lo_menu_set_submenu_action_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand );
700 : else
701 0 : g_lo_menu_set_action_and_target_value_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand, pTarget );
702 :
703 0 : g_free( aItemCommand );
704 : }
705 :
706 0 : if ( aCurrentCommand )
707 0 : g_free( aCurrentCommand );
708 0 : }
709 :
710 0 : GtkSalMenu* GtkSalMenu::GetMenuForItemCommand( gchar* aCommand, gboolean bGetSubmenu )
711 : {
712 0 : SolarMutexGuard aGuard;
713 0 : GtkSalMenu* pMenu = NULL;
714 0 : for ( sal_uInt16 nPos = 0; nPos < maItems.size(); nPos++ )
715 : {
716 0 : GtkSalMenuItem *pSalItem = maItems[ nPos ];
717 :
718 0 : OUString aItemCommand = mpVCLMenu->GetItemCommand( pSalItem->mnId );
719 : // Do not join the following two lines, or the OString will be destroyed
720 : // immediately, and the gchar* pointed to by aItemCommandStr will be
721 : // freed before it can be used - fdo#69090
722 0 : OString aItemCommandOStr = OUStringToOString( aItemCommand, RTL_TEXTENCODING_UTF8 );
723 0 : gchar* aItemCommandStr = (gchar*) aItemCommandOStr.getStr();
724 :
725 0 : if ( g_strcmp0( aItemCommandStr, aCommand ) == 0 )
726 : {
727 0 : pMenu = bGetSubmenu ? pSalItem->mpSubMenu : this;
728 0 : break;
729 : }
730 : else
731 : {
732 0 : if ( pSalItem->mpSubMenu != NULL )
733 0 : pMenu = pSalItem->mpSubMenu->GetMenuForItemCommand( aCommand, bGetSubmenu );
734 :
735 0 : if ( pMenu != NULL )
736 0 : break;
737 : }
738 0 : }
739 :
740 0 : return pMenu;
741 : }
742 :
743 0 : void GtkSalMenu::DispatchCommand( gint itemId, const gchar *aCommand )
744 : {
745 0 : SolarMutexGuard aGuard;
746 : // Only the menubar is allowed to dispatch commands.
747 0 : if ( !mbMenuBar )
748 0 : return;
749 :
750 0 : GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( (gchar*) aCommand, FALSE );
751 0 : Menu* pSubMenu = ( pSalSubMenu != NULL ) ? pSalSubMenu->GetMenu() : NULL;
752 :
753 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
754 0 : pMenuBar->HandleMenuCommandEvent( pSubMenu, itemId );
755 : }
756 :
757 0 : void GtkSalMenu::ActivateAllSubmenus(MenuBar* pMenuBar)
758 : {
759 0 : pMenuBar->HandleMenuActivateEvent(mpVCLMenu);
760 0 : for ( sal_uInt16 nPos = 0; nPos < maItems.size(); nPos++ )
761 : {
762 0 : GtkSalMenuItem *pSalItem = maItems[ nPos ];
763 0 : if ( pSalItem->mpSubMenu != NULL )
764 : {
765 0 : pSalItem->mpSubMenu->ActivateAllSubmenus(pMenuBar);
766 0 : pSalItem->mpSubMenu->Update();
767 : }
768 : }
769 0 : }
770 :
771 0 : void GtkSalMenu::Activate()
772 : {
773 0 : if ( !mbMenuBar )
774 0 : return;
775 0 : ActivateAllSubmenus(static_cast<MenuBar*>(mpVCLMenu));
776 : }
777 :
778 0 : void GtkSalMenu::Deactivate( const gchar* aMenuCommand )
779 : {
780 0 : if ( !mbMenuBar )
781 0 : return;
782 :
783 0 : GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( (gchar*) aMenuCommand, TRUE );
784 :
785 0 : if ( pSalSubMenu != NULL ) {
786 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
787 0 : pMenuBar->HandleMenuDeActivateEvent( pSalSubMenu->mpVCLMenu );
788 : }
789 : }
790 :
791 0 : void GtkSalMenu::Display( bool bVisible )
792 : {
793 0 : if ( !mbMenuBar || mpVCLMenu == NULL )
794 0 : return;
795 :
796 0 : bMenuVisibility = bVisible;
797 :
798 0 : bool bVCLMenuVisible = ( bVisible ) ? false : true;
799 :
800 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
801 0 : pMenuBar->SetDisplayable( bVCLMenuVisible );
802 : }
803 :
804 0 : bool GtkSalMenu::IsItemVisible( unsigned nPos )
805 : {
806 0 : SolarMutexGuard aGuard;
807 0 : bool bVisible = false;
808 :
809 0 : if ( nPos < maItems.size() )
810 0 : bVisible = ( ( GtkSalMenuItem* ) maItems[ nPos ])->mbVisible;
811 :
812 0 : return bVisible;
813 : }
814 :
815 0 : void GtkSalMenu::CheckItem( unsigned, bool )
816 : {
817 0 : }
818 :
819 0 : void GtkSalMenu::EnableItem( unsigned, bool )
820 : {
821 0 : }
822 :
823 0 : void GtkSalMenu::ShowItem( unsigned nPos, bool bShow )
824 : {
825 0 : SolarMutexGuard aGuard;
826 0 : if ( nPos < maItems.size() )
827 0 : ( ( GtkSalMenuItem* ) maItems[ nPos ] )->mbVisible = bShow;
828 0 : }
829 :
830 0 : void GtkSalMenu::SetItemText( unsigned, SalMenuItem*, const OUString& )
831 : {
832 0 : }
833 :
834 0 : void GtkSalMenu::SetItemImage( unsigned, SalMenuItem*, const Image& )
835 : {
836 0 : }
837 :
838 0 : void GtkSalMenu::SetAccelerator( unsigned, SalMenuItem*, const vcl::KeyCode&, const OUString& )
839 : {
840 0 : }
841 :
842 0 : void GtkSalMenu::GetSystemMenuData( SystemMenuData* )
843 : {
844 0 : }
845 :
846 : /*
847 : * GtkSalMenuItem
848 : */
849 :
850 0 : GtkSalMenuItem::GtkSalMenuItem( const SalItemParams* pItemData ) :
851 : mnId( pItemData->nId ),
852 : mnType( pItemData->eType ),
853 : mbVisible( true ),
854 : mpVCLMenu( pItemData->pMenu ),
855 : mpParentMenu( NULL ),
856 0 : mpSubMenu( NULL )
857 : {
858 0 : }
859 :
860 0 : GtkSalMenuItem::~GtkSalMenuItem()
861 : {
862 0 : }
863 :
864 : #endif
865 :
866 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|