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