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 = const_cast<GtkSalFrame*>(pFrame);
163 0 : GtkSalMenu* pSalMenu = 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( static_cast<const gchar*>(str1), static_cast<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, reinterpret_cast<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 = static_cast<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 43 : GtkSalMenu::GtkSalMenu( bool bMenuBar ) :
394 : mbMenuBar( bMenuBar ),
395 : mpVCLMenu( NULL ),
396 : mpParentSalMenu( NULL ),
397 : mpFrame( NULL ),
398 : mpMenuModel( NULL ),
399 43 : mpActionGroup( NULL )
400 : {
401 43 : }
402 :
403 126 : GtkSalMenu::~GtkSalMenu()
404 : {
405 42 : SolarMutexGuard aGuard;
406 :
407 42 : if ( mbMenuBar )
408 : {
409 2 : if ( mpMenuModel )
410 : {
411 : // g_lo_menu_remove( G_LO_MENU( mpMenuModel ), 0 );
412 2 : g_object_unref( mpMenuModel );
413 : }
414 : }
415 :
416 42 : maItems.clear();
417 84 : }
418 :
419 4 : bool GtkSalMenu::VisibleMenuBar()
420 : {
421 4 : return bMenuVisibility;
422 : }
423 :
424 136 : void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
425 : {
426 136 : SolarMutexGuard aGuard;
427 136 : GtkSalMenuItem *pItem = static_cast<GtkSalMenuItem*>( pSalMenuItem );
428 :
429 136 : if ( nPos == MENU_APPEND )
430 136 : maItems.push_back( pItem );
431 : else
432 0 : maItems.insert( maItems.begin() + nPos, pItem );
433 :
434 136 : pItem->mpParentMenu = this;
435 136 : }
436 :
437 0 : void GtkSalMenu::RemoveItem( unsigned nPos )
438 : {
439 0 : SolarMutexGuard aGuard;
440 0 : maItems.erase( maItems.begin() + nPos );
441 0 : }
442 :
443 24 : void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned )
444 : {
445 24 : SolarMutexGuard aGuard;
446 24 : GtkSalMenuItem *pItem = static_cast< GtkSalMenuItem* >( pSalMenuItem );
447 24 : GtkSalMenu *pGtkSubMenu = static_cast< GtkSalMenu* >( pSubMenu );
448 :
449 24 : if ( pGtkSubMenu == NULL )
450 29 : return;
451 :
452 19 : pGtkSubMenu->mpParentSalMenu = this;
453 19 : pItem->mpSubMenu = pGtkSubMenu;
454 : }
455 :
456 : static bool bInvalidMenus = false;
457 0 : static gboolean RefreshMenusUnity(gpointer)
458 : {
459 0 : SolarMutexGuard g;
460 :
461 0 : SalDisplay* pSalDisplay = vcl_sal::getSalDisplay(GetGenericData());
462 0 : std::list< SalFrame* >::const_iterator pSalFrame = pSalDisplay->getFrames().begin();
463 0 : std::list< SalFrame* >::const_iterator pEndSalFrame = pSalDisplay->getFrames().end();
464 0 : for(; pSalFrame != pEndSalFrame; ++pSalFrame) {
465 0 : const GtkSalFrame* pGtkSalFrame = static_cast< const GtkSalFrame* >( *pSalFrame );
466 0 : GtkSalFrame* pFrameNonConst = const_cast<GtkSalFrame*>(pGtkSalFrame);
467 0 : GtkSalMenu* pSalMenu = static_cast<GtkSalMenu*>(pFrameNonConst->GetMenu());
468 0 : if(pSalMenu) {
469 0 : pSalMenu->Activate();
470 0 : pSalMenu->UpdateFull();
471 : }
472 : }
473 0 : bInvalidMenus = false;
474 0 : return FALSE;
475 : }
476 :
477 0 : static long RefreshMenusUnity(void*, void*)
478 : {
479 0 : if(!bInvalidMenus) {
480 0 : g_timeout_add(10, &RefreshMenusUnity, NULL);
481 0 : bInvalidMenus = true;
482 : }
483 0 : return 0;
484 : }
485 :
486 2 : static Link<>* getRefreshLinkInstance()
487 : {
488 : static Link<>* pLink = NULL;
489 2 : if(!pLink) {
490 2 : pLink = new Link<>(NULL, &RefreshMenusUnity);
491 : }
492 2 : return pLink;
493 : }
494 :
495 2 : void GtkSalMenu::SetFrame( const SalFrame* pFrame )
496 : {
497 2 : SolarMutexGuard aGuard;
498 : {
499 2 : vcl::MenuInvalidator::GetMenuInvalidateListeners()->addListener(*getRefreshLinkInstance());
500 : }
501 :
502 : assert(mbMenuBar);
503 : SAL_INFO("vcl.unity", "GtkSalMenu set to frame");
504 2 : mpFrame = static_cast< const GtkSalFrame* >( pFrame );
505 2 : GtkSalFrame* pFrameNonConst = const_cast<GtkSalFrame*>(mpFrame);
506 :
507 : // if we had a menu on the GtkSalMenu we have to free it as we generate a
508 : // full menu anyway and we might need to reuse an existing model and
509 : // actiongroup
510 2 : pFrameNonConst->SetMenu( this );
511 2 : pFrameNonConst->EnsureAppMenuWatch();
512 :
513 : // Clean menu model and action group if needed.
514 2 : GtkWidget* pWidget = pFrameNonConst->getWindow();
515 2 : GdkWindow* gdkWindow = gtk_widget_get_window( pWidget );
516 :
517 2 : GLOMenu* pMenuModel = G_LO_MENU( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) );
518 2 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-action-group" ) );
519 : SAL_INFO("vcl.unity", "Found menu model: " << pMenuModel << " and action group: " << pActionGroup);
520 :
521 2 : if ( pMenuModel )
522 : {
523 2 : if ( g_menu_model_get_n_items( G_MENU_MODEL( pMenuModel ) ) > 0 )
524 0 : g_lo_menu_remove( pMenuModel, 0 );
525 :
526 2 : mpMenuModel = G_MENU_MODEL( g_lo_menu_new() );
527 : }
528 :
529 2 : if ( pActionGroup )
530 : {
531 2 : g_lo_action_group_clear( pActionGroup );
532 2 : mpActionGroup = G_ACTION_GROUP( pActionGroup );
533 : }
534 :
535 : // Generate the main menu structure.
536 2 : if (bMenuVisibility)
537 0 : UpdateFull();
538 :
539 2 : g_lo_menu_insert_section( pMenuModel, 0, NULL, mpMenuModel );
540 2 : }
541 :
542 0 : const GtkSalFrame* GtkSalMenu::GetFrame() const
543 : {
544 0 : SolarMutexGuard aGuard;
545 0 : const GtkSalMenu* pMenu = this;
546 0 : while( pMenu && ! pMenu->mpFrame )
547 0 : pMenu = pMenu->mpParentSalMenu;
548 0 : return pMenu ? pMenu->mpFrame : NULL;
549 : }
550 :
551 0 : void GtkSalMenu::NativeCheckItem( unsigned nSection, unsigned nItemPos, MenuItemBits bits, gboolean bCheck )
552 : {
553 0 : SolarMutexGuard aGuard;
554 :
555 0 : if ( mpActionGroup == NULL )
556 0 : return;
557 :
558 0 : gchar* aCommand = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
559 :
560 0 : if ( aCommand != NULL || g_strcmp0( aCommand, "" ) != 0 )
561 : {
562 0 : GVariant *pCheckValue = NULL;
563 0 : GVariant *pCurrentState = g_action_group_get_action_state( mpActionGroup, aCommand );
564 :
565 0 : if ( bits & MenuItemBits::RADIOCHECK )
566 0 : pCheckValue = bCheck ? g_variant_new_string( aCommand ) : g_variant_new_string( "" );
567 : else
568 : {
569 : // By default, all checked items are checkmark buttons.
570 0 : if ( bCheck || ( !bCheck && pCurrentState != NULL ) )
571 0 : pCheckValue = g_variant_new_boolean( bCheck );
572 : }
573 :
574 0 : if ( pCheckValue != NULL )
575 : {
576 0 : if ( pCurrentState == NULL || g_variant_equal( pCurrentState, pCheckValue ) == FALSE )
577 : {
578 0 : g_action_group_change_action_state( mpActionGroup, aCommand, pCheckValue );
579 : }
580 : else
581 : {
582 0 : g_variant_unref (pCheckValue);
583 : }
584 : }
585 :
586 0 : if ( pCurrentState != NULL )
587 0 : g_variant_unref( pCurrentState );
588 : }
589 :
590 0 : if ( aCommand )
591 0 : g_free( aCommand );
592 : }
593 :
594 0 : void GtkSalMenu::NativeSetEnableItem( gchar* aCommand, gboolean bEnable )
595 : {
596 0 : SolarMutexGuard aGuard;
597 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
598 :
599 0 : if ( g_action_group_get_action_enabled( G_ACTION_GROUP( pActionGroup ), aCommand ) != bEnable )
600 0 : g_lo_action_group_set_action_enabled( pActionGroup, aCommand, bEnable );
601 0 : }
602 :
603 0 : void GtkSalMenu::NativeSetItemText( unsigned nSection, unsigned nItemPos, const OUString& rText )
604 : {
605 0 : SolarMutexGuard aGuard;
606 : // Escape all underscores so that they don't get interpreted as hotkeys
607 0 : OUString aText = rText.replaceAll( "_", "__" );
608 : // Replace the LibreOffice hotkey identifier with an underscore
609 0 : aText = aText.replace( '~', '_' );
610 0 : OString aConvertedText = OUStringToOString( aText, RTL_TEXTENCODING_UTF8 );
611 :
612 : // Update item text only when necessary.
613 0 : gchar* aLabel = g_lo_menu_get_label_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
614 :
615 0 : if ( aLabel == NULL || g_strcmp0( aLabel, aConvertedText.getStr() ) != 0 )
616 0 : g_lo_menu_set_label_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aConvertedText.getStr() );
617 :
618 0 : if ( aLabel )
619 0 : g_free( aLabel );
620 0 : }
621 :
622 0 : void GtkSalMenu::NativeSetAccelerator( unsigned nSection, unsigned nItemPos, const vcl::KeyCode& rKeyCode, const OUString& rKeyName )
623 : {
624 0 : SolarMutexGuard aGuard;
625 :
626 0 : if ( rKeyName.isEmpty() )
627 0 : return;
628 :
629 : guint nKeyCode;
630 : GdkModifierType nModifiers;
631 :
632 0 : KeyCodeToGdkKey( rKeyCode, &nKeyCode, &nModifiers );
633 :
634 0 : gchar* aAccelerator = gtk_accelerator_name( nKeyCode, nModifiers );
635 :
636 0 : gchar* aCurrentAccel = g_lo_menu_get_accelerator_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
637 :
638 0 : if ( aCurrentAccel == NULL && g_strcmp0( aCurrentAccel, aAccelerator ) != 0 )
639 0 : g_lo_menu_set_accelerator_to_item_in_section ( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aAccelerator );
640 :
641 0 : g_free( aAccelerator );
642 0 : g_free( aCurrentAccel );
643 : }
644 :
645 0 : void GtkSalMenu::NativeSetItemCommand( unsigned nSection,
646 : unsigned nItemPos,
647 : sal_uInt16 nId,
648 : const gchar* aCommand,
649 : MenuItemBits nBits,
650 : gboolean bChecked,
651 : gboolean bIsSubmenu )
652 : {
653 0 : SolarMutexGuard aGuard;
654 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
655 :
656 0 : GVariant *pTarget = NULL;
657 :
658 0 : if ( g_action_group_has_action( mpActionGroup, aCommand ) == FALSE ) {
659 0 : if ( ( nBits & MenuItemBits::CHECKABLE ) || bIsSubmenu )
660 : {
661 : // Item is a checkmark button.
662 0 : GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_BOOLEAN) );
663 0 : GVariant* pState = g_variant_new_boolean( bChecked );
664 :
665 0 : g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, bIsSubmenu, NULL, pStateType, NULL, pState );
666 : }
667 0 : else if ( nBits & MenuItemBits::RADIOCHECK )
668 : {
669 : // Item is a radio button.
670 0 : GVariantType* pParameterType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) );
671 0 : GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) );
672 0 : GVariant* pState = g_variant_new_string( "" );
673 0 : pTarget = g_variant_new_string( aCommand );
674 :
675 0 : g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, FALSE, pParameterType, pStateType, NULL, pState );
676 : }
677 : else
678 : {
679 : // Item is not special, so insert a stateless action.
680 0 : g_lo_action_group_insert( pActionGroup, aCommand, nId, FALSE );
681 : }
682 : }
683 :
684 0 : GLOMenu* pMenu = G_LO_MENU( mpMenuModel );
685 :
686 : // Menu item is not updated unless it's necessary.
687 0 : gchar* aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, nItemPos );
688 :
689 0 : if ( aCurrentCommand == NULL || g_strcmp0( aCurrentCommand, aCommand ) != 0 )
690 : {
691 0 : g_lo_menu_set_command_to_item_in_section( pMenu, nSection, nItemPos, aCommand );
692 :
693 0 : gchar* aItemCommand = g_strconcat("win.", aCommand, NULL );
694 :
695 0 : if ( bIsSubmenu )
696 0 : g_lo_menu_set_submenu_action_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand );
697 : else
698 0 : g_lo_menu_set_action_and_target_value_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand, pTarget );
699 :
700 0 : g_free( aItemCommand );
701 : }
702 :
703 0 : if ( aCurrentCommand )
704 0 : g_free( aCurrentCommand );
705 0 : }
706 :
707 0 : GtkSalMenu* GtkSalMenu::GetMenuForItemCommand( gchar* aCommand, gboolean bGetSubmenu )
708 : {
709 0 : SolarMutexGuard aGuard;
710 0 : GtkSalMenu* pMenu = NULL;
711 0 : for ( size_t nPos = 0; nPos < maItems.size(); nPos++ )
712 : {
713 0 : GtkSalMenuItem *pSalItem = maItems[ nPos ];
714 :
715 0 : OUString aItemCommand = mpVCLMenu->GetItemCommand( pSalItem->mnId );
716 : // Do not join the following two lines, or the OString will be destroyed
717 : // immediately, and the gchar* pointed to by aItemCommandStr will be
718 : // freed before it can be used - fdo#69090
719 0 : OString aItemCommandOStr = OUStringToOString( aItemCommand, RTL_TEXTENCODING_UTF8 );
720 0 : gchar* aItemCommandStr = const_cast<gchar*>(aItemCommandOStr.getStr());
721 :
722 0 : if ( g_strcmp0( aItemCommandStr, aCommand ) == 0 )
723 : {
724 0 : pMenu = bGetSubmenu ? pSalItem->mpSubMenu : this;
725 0 : break;
726 : }
727 : else
728 : {
729 0 : if ( pSalItem->mpSubMenu != NULL )
730 0 : pMenu = pSalItem->mpSubMenu->GetMenuForItemCommand( aCommand, bGetSubmenu );
731 :
732 0 : if ( pMenu != NULL )
733 0 : break;
734 : }
735 0 : }
736 :
737 0 : return pMenu;
738 : }
739 :
740 0 : void GtkSalMenu::DispatchCommand( gint itemId, const gchar *aCommand )
741 : {
742 0 : SolarMutexGuard aGuard;
743 : // Only the menubar is allowed to dispatch commands.
744 0 : if ( !mbMenuBar )
745 0 : return;
746 :
747 0 : GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aCommand), FALSE );
748 0 : Menu* pSubMenu = ( pSalSubMenu != NULL ) ? pSalSubMenu->GetMenu() : NULL;
749 :
750 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
751 0 : pMenuBar->HandleMenuCommandEvent( pSubMenu, itemId );
752 : }
753 :
754 0 : void GtkSalMenu::ActivateAllSubmenus(MenuBar* pMenuBar)
755 : {
756 0 : pMenuBar->HandleMenuActivateEvent(mpVCLMenu);
757 0 : for ( size_t nPos = 0; nPos < maItems.size(); nPos++ )
758 : {
759 0 : GtkSalMenuItem *pSalItem = maItems[ nPos ];
760 0 : if ( pSalItem->mpSubMenu != NULL )
761 : {
762 0 : pSalItem->mpSubMenu->ActivateAllSubmenus(pMenuBar);
763 0 : pSalItem->mpSubMenu->Update();
764 : }
765 : }
766 0 : }
767 :
768 0 : void GtkSalMenu::Activate()
769 : {
770 0 : if ( !mbMenuBar )
771 0 : return;
772 0 : ActivateAllSubmenus(static_cast<MenuBar*>(mpVCLMenu));
773 : }
774 :
775 0 : void GtkSalMenu::Deactivate( const gchar* aMenuCommand )
776 : {
777 0 : if ( !mbMenuBar )
778 0 : return;
779 :
780 0 : GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aMenuCommand), TRUE );
781 :
782 0 : if ( pSalSubMenu != NULL ) {
783 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
784 0 : pMenuBar->HandleMenuDeActivateEvent( pSalSubMenu->mpVCLMenu );
785 : }
786 : }
787 :
788 0 : void GtkSalMenu::Display( bool bVisible )
789 : {
790 0 : if ( !mbMenuBar || mpVCLMenu == NULL )
791 0 : return;
792 :
793 0 : bMenuVisibility = bVisible;
794 :
795 0 : bool bVCLMenuVisible = !bVisible;
796 :
797 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
798 0 : pMenuBar->SetDisplayable( bVCLMenuVisible );
799 : }
800 :
801 0 : bool GtkSalMenu::IsItemVisible( unsigned nPos )
802 : {
803 0 : SolarMutexGuard aGuard;
804 0 : bool bVisible = false;
805 :
806 0 : if ( nPos < maItems.size() )
807 0 : bVisible = maItems[ nPos ]->mbVisible;
808 :
809 0 : return bVisible;
810 : }
811 :
812 0 : void GtkSalMenu::CheckItem( unsigned, bool )
813 : {
814 0 : }
815 :
816 5 : void GtkSalMenu::EnableItem( unsigned, bool )
817 : {
818 5 : }
819 :
820 0 : void GtkSalMenu::ShowItem( unsigned nPos, bool bShow )
821 : {
822 0 : SolarMutexGuard aGuard;
823 0 : if ( nPos < maItems.size() )
824 0 : maItems[ nPos ]->mbVisible = bShow;
825 0 : }
826 :
827 7 : void GtkSalMenu::SetItemText( unsigned, SalMenuItem*, const OUString& )
828 : {
829 7 : }
830 :
831 36 : void GtkSalMenu::SetItemImage( unsigned, SalMenuItem*, const Image& )
832 : {
833 36 : }
834 :
835 0 : void GtkSalMenu::SetAccelerator( unsigned, SalMenuItem*, const vcl::KeyCode&, const OUString& )
836 : {
837 0 : }
838 :
839 0 : void GtkSalMenu::GetSystemMenuData( SystemMenuData* )
840 : {
841 0 : }
842 :
843 : /*
844 : * GtkSalMenuItem
845 : */
846 :
847 136 : GtkSalMenuItem::GtkSalMenuItem( const SalItemParams* pItemData ) :
848 : mnId( pItemData->nId ),
849 : mnType( pItemData->eType ),
850 : mbVisible( true ),
851 : mpVCLMenu( pItemData->pMenu ),
852 : mpParentMenu( NULL ),
853 136 : mpSubMenu( NULL )
854 : {
855 136 : }
856 :
857 272 : GtkSalMenuItem::~GtkSalMenuItem()
858 : {
859 281 : }
860 :
861 : #endif
862 :
863 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|