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 sal_Bool bMenuVisibility = sal_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 ) == sal_False )
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 : String aText = pVCLMenu->GetItemText( nId );
303 0 : sal_Bool itemEnabled = pVCLMenu->IsItemEnabled( nId );
304 0 : KeyCode nAccelKey = pVCLMenu->GetAccelKey( nId );
305 0 : sal_Bool itemChecked = pVCLMenu->IsItemChecked( nId );
306 0 : MenuItemBits itemBits = pVCLMenu->GetItemBits( nId );
307 :
308 : // Convert internal values to native values.
309 0 : gboolean bChecked = ( itemChecked == sal_True ) ? TRUE : FALSE;
310 0 : gboolean bEnabled = ( itemEnabled == sal_True ) ? TRUE : FALSE;
311 :
312 : // Store current item command in command list.
313 0 : gchar *aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pLOMenu, nSection, nItemPos );
314 :
315 0 : if ( aCurrentCommand != NULL )
316 0 : pOldCommandList = g_list_append( pOldCommandList, aCurrentCommand );
317 :
318 : // Get the new command for the item.
319 0 : gchar* aNativeCommand = GetCommandForItem( pSalMenuItem, aCurrentCommand, mpActionGroup );
320 :
321 : // Force updating of native menu labels.
322 0 : NativeSetItemText( nSection, nItemPos, aText );
323 0 : NativeSetAccelerator( nSection, nItemPos, nAccelKey, nAccelKey.GetName( GetFrame()->GetWindow() ) );
324 :
325 0 : if ( g_strcmp0( aNativeCommand, "" ) != 0 && pSalMenuItem->mpSubMenu == NULL )
326 : {
327 0 : NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, bChecked, FALSE );
328 0 : NativeCheckItem( nSection, nItemPos, itemBits, bChecked );
329 0 : NativeSetEnableItem( aNativeCommand, bEnabled );
330 :
331 0 : pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) );
332 : }
333 :
334 0 : GtkSalMenu* pSubmenu = pSalMenuItem->mpSubMenu;
335 :
336 0 : if ( pSubmenu && pSubmenu->GetMenu() )
337 : {
338 0 : NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, FALSE, TRUE );
339 0 : pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) );
340 :
341 0 : GLOMenu* pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos );
342 :
343 0 : if ( pSubMenuModel == NULL )
344 : {
345 0 : g_lo_menu_new_submenu_in_item_in_section( pLOMenu, nSection, nItemPos );
346 0 : pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos );
347 : }
348 :
349 0 : g_object_unref( pSubMenuModel );
350 :
351 0 : if ( bRecurse )
352 : {
353 : SAL_INFO("vcl.unity", "preparing submenu " << pSubMenuModel << " to menu model " << G_MENU_MODEL(pSubMenuModel) << " and action group " << G_ACTION_GROUP(pActionGroup));
354 0 : pSubmenu->SetMenuModel( G_MENU_MODEL( pSubMenuModel ) );
355 0 : pSubmenu->SetActionGroup( G_ACTION_GROUP( pActionGroup ) );
356 0 : pSubmenu->ImplUpdate( bRecurse );
357 : }
358 : }
359 :
360 0 : g_free( aNativeCommand );
361 :
362 0 : ++nItemPos;
363 0 : ++validItems;
364 0 : }
365 :
366 : // Delete extra items in last section.
367 0 : RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems );
368 :
369 : // Delete extra sections.
370 0 : RemoveSpareSectionsFromNativeMenu( pLOMenu, &pOldCommandList, nSection );
371 :
372 : // Delete unused commands.
373 0 : RemoveUnusedCommands( pActionGroup, pOldCommandList, pNewCommandList );
374 : }
375 :
376 0 : void GtkSalMenu::Update()
377 : {
378 0 : ImplUpdate( FALSE );
379 0 : }
380 :
381 0 : void GtkSalMenu::UpdateFull()
382 : {
383 0 : ImplUpdate( TRUE );
384 0 : }
385 :
386 :
387 : /*
388 : * GtkSalMenu
389 : */
390 :
391 0 : GtkSalMenu::GtkSalMenu( sal_Bool bMenuBar ) :
392 : mbMenuBar( bMenuBar ),
393 : mpVCLMenu( NULL ),
394 : mpOldSalMenu( NULL ),
395 : mpParentSalMenu( NULL ),
396 : mpFrame( NULL ),
397 : mpMenuModel( NULL ),
398 0 : mpActionGroup( NULL )
399 : {
400 0 : }
401 :
402 0 : GtkSalMenu::~GtkSalMenu()
403 : {
404 0 : SolarMutexGuard aGuard;
405 :
406 0 : if ( mbMenuBar == sal_True )
407 : {
408 0 : if ( mpMenuModel )
409 : {
410 : // g_lo_menu_remove( G_LO_MENU( mpMenuModel ), 0 );
411 0 : g_object_unref( mpMenuModel );
412 : }
413 : }
414 :
415 0 : maItems.clear();
416 0 : }
417 :
418 0 : sal_Bool GtkSalMenu::VisibleMenuBar()
419 : {
420 0 : return bMenuVisibility;
421 : }
422 :
423 0 : void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
424 : {
425 0 : SolarMutexGuard aGuard;
426 0 : GtkSalMenuItem *pItem = static_cast<GtkSalMenuItem*>( pSalMenuItem );
427 :
428 0 : if ( nPos == MENU_APPEND )
429 0 : maItems.push_back( pItem );
430 : else
431 0 : maItems.insert( maItems.begin() + nPos, pItem );
432 :
433 0 : pItem->mpParentMenu = this;
434 0 : }
435 :
436 0 : void GtkSalMenu::RemoveItem( unsigned nPos )
437 : {
438 0 : SolarMutexGuard aGuard;
439 0 : maItems.erase( maItems.begin() + nPos );
440 0 : }
441 :
442 0 : void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned )
443 : {
444 0 : SolarMutexGuard aGuard;
445 0 : GtkSalMenuItem *pItem = static_cast< GtkSalMenuItem* >( pSalMenuItem );
446 0 : GtkSalMenu *pGtkSubMenu = static_cast< GtkSalMenu* >( pSubMenu );
447 :
448 0 : if ( pGtkSubMenu == NULL )
449 0 : return;
450 :
451 0 : pGtkSubMenu->mpParentSalMenu = this;
452 0 : pItem->mpSubMenu = pGtkSubMenu;
453 : }
454 :
455 0 : void GtkSalMenu::SetFrame( const SalFrame* pFrame )
456 : {
457 0 : SolarMutexGuard aGuard;
458 :
459 : assert(mbMenuBar);
460 : SAL_INFO("vcl.unity", "GtkSalMenu set to frame");
461 0 : mpFrame = static_cast< const GtkSalFrame* >( pFrame );
462 0 : GtkSalFrame* pFrameNonConst = const_cast<GtkSalFrame*>(mpFrame);
463 :
464 : // if we had a menu on the GtkSalMenu we have to free it as we generate a
465 : // full menu anyway and we might need to reuse an existing model and
466 : // actiongroup
467 0 : mpOldSalMenu = static_cast< GtkSalMenu* >( pFrameNonConst->GetMenu() );
468 0 : pFrameNonConst->SetMenu( this );
469 0 : pFrameNonConst->EnsureAppMenuWatch();
470 :
471 : // Clean menu model and action group if needed.
472 0 : GtkWidget* pWidget = pFrameNonConst->getWindow();
473 0 : GdkWindow* gdkWindow = gtk_widget_get_window( pWidget );
474 :
475 0 : GLOMenu* pMenuModel = G_LO_MENU( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) );
476 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-action-group" ) );
477 : SAL_INFO("vcl.unity", "Found menu model: " << pMenuModel << " and action group: " << pActionGroup);
478 :
479 0 : if ( pMenuModel )
480 : {
481 0 : if ( g_menu_model_get_n_items( G_MENU_MODEL( pMenuModel ) ) > 0 )
482 0 : g_lo_menu_remove( pMenuModel, 0 );
483 :
484 0 : mpMenuModel = G_MENU_MODEL( g_lo_menu_new() );
485 : }
486 :
487 0 : if ( pActionGroup )
488 : {
489 0 : g_lo_action_group_clear( pActionGroup );
490 0 : mpActionGroup = G_ACTION_GROUP( pActionGroup );
491 : }
492 :
493 : // Generate the main menu structure.
494 0 : if (bMenuVisibility)
495 0 : UpdateFull();
496 :
497 0 : g_lo_menu_insert_section( pMenuModel, 0, NULL, mpMenuModel );
498 0 : }
499 :
500 0 : const GtkSalFrame* GtkSalMenu::GetFrame() const
501 : {
502 0 : SolarMutexGuard aGuard;
503 0 : const GtkSalMenu* pMenu = this;
504 0 : while( pMenu && ! pMenu->mpFrame )
505 0 : pMenu = pMenu->mpParentSalMenu;
506 0 : return pMenu ? pMenu->mpFrame : NULL;
507 : }
508 :
509 0 : void GtkSalMenu::NativeCheckItem( unsigned nSection, unsigned nItemPos, MenuItemBits bits, gboolean bCheck )
510 : {
511 0 : SolarMutexGuard aGuard;
512 :
513 0 : if ( mpActionGroup == NULL )
514 0 : return;
515 :
516 0 : gchar* aCommand = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
517 :
518 0 : if ( aCommand != NULL || g_strcmp0( aCommand, "" ) != 0 )
519 : {
520 0 : GVariant *pCheckValue = NULL;
521 0 : GVariant *pCurrentState = g_action_group_get_action_state( mpActionGroup, aCommand );
522 :
523 0 : if ( bits & MIB_RADIOCHECK )
524 0 : pCheckValue = bCheck ? g_variant_new_string( aCommand ) : g_variant_new_string( "" );
525 : else
526 : {
527 : // By default, all checked items are checkmark buttons.
528 0 : if ( bCheck || ( !bCheck && pCurrentState != NULL ) )
529 0 : pCheckValue = g_variant_new_boolean( bCheck );
530 : }
531 :
532 0 : if ( pCheckValue != NULL && ( pCurrentState == NULL || g_variant_equal( pCurrentState, pCheckValue ) == FALSE ) )
533 0 : g_action_group_change_action_state( mpActionGroup, aCommand, pCheckValue );
534 :
535 0 : if ( pCurrentState != NULL )
536 0 : g_variant_unref( pCurrentState );
537 : }
538 :
539 0 : if ( aCommand )
540 0 : g_free( aCommand );
541 : }
542 :
543 0 : void GtkSalMenu::NativeSetEnableItem( gchar* aCommand, gboolean bEnable )
544 : {
545 0 : SolarMutexGuard aGuard;
546 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
547 :
548 0 : if ( g_action_group_get_action_enabled( G_ACTION_GROUP( pActionGroup ), aCommand ) != bEnable )
549 0 : g_lo_action_group_set_action_enabled( pActionGroup, aCommand, bEnable );
550 0 : }
551 :
552 0 : void GtkSalMenu::NativeSetItemText( unsigned nSection, unsigned nItemPos, const OUString& rText )
553 : {
554 0 : SolarMutexGuard aGuard;
555 : // Replace the '~' character with '_'.
556 0 : OUString aText = rText.replace( '~', '_' );
557 0 : OString aConvertedText = OUStringToOString( aText, RTL_TEXTENCODING_UTF8 );
558 :
559 : // Update item text only when necessary.
560 0 : gchar* aLabel = g_lo_menu_get_label_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
561 :
562 0 : if ( aLabel == NULL || g_strcmp0( aLabel, aConvertedText.getStr() ) != 0 )
563 0 : g_lo_menu_set_label_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aConvertedText.getStr() );
564 :
565 0 : if ( aLabel )
566 0 : g_free( aLabel );
567 0 : }
568 :
569 0 : void GtkSalMenu::NativeSetAccelerator( unsigned nSection, unsigned nItemPos, const KeyCode& rKeyCode, const OUString& rKeyName )
570 : {
571 0 : SolarMutexGuard aGuard;
572 :
573 0 : if ( rKeyName.isEmpty() )
574 0 : return;
575 :
576 : guint nKeyCode;
577 : GdkModifierType nModifiers;
578 :
579 0 : KeyCodeToGdkKey( rKeyCode, &nKeyCode, &nModifiers );
580 :
581 0 : gchar* aAccelerator = gtk_accelerator_name( nKeyCode, nModifiers );
582 :
583 0 : gchar* aCurrentAccel = g_lo_menu_get_accelerator_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
584 :
585 0 : if ( aCurrentAccel == NULL && g_strcmp0( aCurrentAccel, aAccelerator ) != 0 )
586 0 : g_lo_menu_set_accelerator_to_item_in_section ( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aAccelerator );
587 :
588 0 : g_free( aAccelerator );
589 : }
590 :
591 0 : void GtkSalMenu::NativeSetItemCommand( unsigned nSection,
592 : unsigned nItemPos,
593 : sal_uInt16 nId,
594 : const gchar* aCommand,
595 : MenuItemBits nBits,
596 : gboolean bChecked,
597 : gboolean bIsSubmenu )
598 : {
599 0 : SolarMutexGuard aGuard;
600 0 : GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
601 :
602 0 : GVariant *pTarget = NULL;
603 :
604 0 : if ( g_action_group_has_action( mpActionGroup, aCommand ) == FALSE ) {
605 0 : if ( ( nBits & MIB_CHECKABLE ) || bIsSubmenu )
606 : {
607 : // Item is a checkmark button.
608 0 : GVariantType* pStateType = g_variant_type_new( (gchar*) G_VARIANT_TYPE_BOOLEAN );
609 0 : GVariant* pState = g_variant_new_boolean( bChecked );
610 :
611 0 : g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, bIsSubmenu, NULL, pStateType, NULL, pState );
612 : }
613 0 : else if ( nBits & MIB_RADIOCHECK )
614 : {
615 : // Item is a radio button.
616 0 : GVariantType* pParameterType = g_variant_type_new( (gchar*) G_VARIANT_TYPE_STRING );
617 0 : GVariantType* pStateType = g_variant_type_new( (gchar*) G_VARIANT_TYPE_STRING );
618 0 : GVariant* pState = g_variant_new_string( "" );
619 0 : pTarget = g_variant_new_string( aCommand );
620 :
621 0 : g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, FALSE, pParameterType, pStateType, NULL, pState );
622 : }
623 : else
624 : {
625 : // Item is not special, so insert a stateless action.
626 0 : g_lo_action_group_insert( pActionGroup, aCommand, nId, FALSE );
627 : }
628 : }
629 :
630 0 : GLOMenu* pMenu = G_LO_MENU( mpMenuModel );
631 :
632 : // Menu item is not updated unless it's necessary.
633 0 : gchar* aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, nItemPos );
634 :
635 0 : if ( aCurrentCommand == NULL || g_strcmp0( aCurrentCommand, aCommand ) != 0 )
636 : {
637 0 : g_lo_menu_set_command_to_item_in_section( pMenu, nSection, nItemPos, aCommand );
638 :
639 0 : gchar* aItemCommand = g_strconcat("win.", aCommand, NULL );
640 :
641 0 : if ( bIsSubmenu )
642 0 : g_lo_menu_set_submenu_action_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand );
643 : else
644 0 : g_lo_menu_set_action_and_target_value_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand, pTarget );
645 :
646 0 : g_free( aItemCommand );
647 : }
648 :
649 0 : if ( aCurrentCommand )
650 0 : g_free( aCurrentCommand );
651 0 : }
652 :
653 0 : GtkSalMenu* GtkSalMenu::GetMenuForItemCommand( gchar* aCommand, gboolean bGetSubmenu )
654 : {
655 0 : SolarMutexGuard aGuard;
656 0 : GtkSalMenu* pMenu = NULL;
657 0 : for ( sal_uInt16 nPos = 0; nPos < maItems.size(); nPos++ )
658 : {
659 0 : GtkSalMenuItem *pSalItem = maItems[ nPos ];
660 :
661 0 : String aItemCommand = mpVCLMenu->GetItemCommand( pSalItem->mnId );
662 0 : gchar* aItemCommandStr = (gchar*) OUStringToOString( aItemCommand, RTL_TEXTENCODING_UTF8 ).getStr();
663 :
664 0 : if ( g_strcmp0( aItemCommandStr, aCommand ) == 0 )
665 : {
666 0 : pMenu = bGetSubmenu ? pSalItem->mpSubMenu : this;
667 0 : break;
668 : }
669 : else
670 : {
671 0 : if ( pSalItem->mpSubMenu != NULL )
672 0 : pMenu = pSalItem->mpSubMenu->GetMenuForItemCommand( aCommand, bGetSubmenu );
673 :
674 0 : if ( pMenu != NULL )
675 0 : break;
676 : }
677 0 : }
678 :
679 0 : return pMenu;
680 : }
681 :
682 0 : void GtkSalMenu::DispatchCommand( gint itemId, const gchar *aCommand )
683 : {
684 0 : SolarMutexGuard aGuard;
685 : // Only the menubar is allowed to dispatch commands.
686 0 : if ( mbMenuBar != TRUE )
687 0 : return;
688 :
689 0 : GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( (gchar*) aCommand, FALSE );
690 0 : Menu* pSubMenu = ( pSalSubMenu != NULL ) ? pSalSubMenu->GetMenu() : NULL;
691 :
692 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
693 0 : pMenuBar->HandleMenuCommandEvent( pSubMenu, itemId );
694 : }
695 :
696 0 : void GtkSalMenu::ActivateAllSubmenus(MenuBar* pMenuBar)
697 : {
698 0 : pMenuBar->HandleMenuActivateEvent(mpVCLMenu);
699 0 : for ( sal_uInt16 nPos = 0; nPos < maItems.size(); nPos++ )
700 : {
701 0 : GtkSalMenuItem *pSalItem = maItems[ nPos ];
702 0 : if ( pSalItem->mpSubMenu != NULL )
703 : {
704 0 : pSalItem->mpSubMenu->ActivateAllSubmenus(pMenuBar);
705 0 : pSalItem->mpSubMenu->Update();
706 : }
707 : }
708 0 : }
709 :
710 0 : void GtkSalMenu::Activate()
711 : {
712 0 : if ( mbMenuBar != TRUE )
713 0 : return;
714 0 : ActivateAllSubmenus(static_cast<MenuBar*>(mpVCLMenu));
715 : }
716 :
717 :
718 0 : void GtkSalMenu::Deactivate( const gchar* aMenuCommand )
719 : {
720 0 : if ( mbMenuBar == sal_False )
721 0 : return;
722 :
723 0 : GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( (gchar*) aMenuCommand, TRUE );
724 :
725 0 : if ( pSalSubMenu != NULL ) {
726 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
727 0 : pMenuBar->HandleMenuDeActivateEvent( pSalSubMenu->mpVCLMenu );
728 : }
729 : }
730 :
731 0 : void GtkSalMenu::Display( sal_Bool bVisible )
732 : {
733 0 : if ( mbMenuBar == sal_False || mpVCLMenu == NULL )
734 0 : return;
735 :
736 0 : bMenuVisibility = bVisible;
737 :
738 0 : sal_Bool bVCLMenuVisible = ( bVisible == sal_True ) ? sal_False : sal_True;
739 :
740 0 : MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
741 0 : pMenuBar->SetDisplayable( bVCLMenuVisible );
742 : }
743 :
744 0 : sal_Bool GtkSalMenu::IsItemVisible( unsigned nPos )
745 : {
746 0 : SolarMutexGuard aGuard;
747 0 : sal_Bool bVisible = sal_False;
748 :
749 0 : if ( nPos < maItems.size() )
750 0 : bVisible = ( ( GtkSalMenuItem* ) maItems[ nPos ])->mbVisible;
751 :
752 0 : return bVisible;
753 : }
754 :
755 0 : void GtkSalMenu::CheckItem( unsigned, sal_Bool )
756 : {
757 0 : }
758 :
759 0 : void GtkSalMenu::EnableItem( unsigned, sal_Bool )
760 : {
761 0 : }
762 :
763 0 : void GtkSalMenu::ShowItem( unsigned nPos, sal_Bool bShow )
764 : {
765 0 : SolarMutexGuard aGuard;
766 0 : if ( nPos < maItems.size() )
767 0 : ( ( GtkSalMenuItem* ) maItems[ nPos ] )->mbVisible = bShow;
768 0 : }
769 :
770 :
771 0 : void GtkSalMenu::SetItemText( unsigned, SalMenuItem*, const OUString& )
772 : {
773 0 : }
774 :
775 0 : void GtkSalMenu::SetItemImage( unsigned, SalMenuItem*, const Image& )
776 : {
777 0 : }
778 :
779 0 : void GtkSalMenu::SetAccelerator( unsigned, SalMenuItem*, const KeyCode&, const OUString& )
780 : {
781 0 : }
782 :
783 0 : void GtkSalMenu::GetSystemMenuData( SystemMenuData* )
784 : {
785 0 : }
786 :
787 : // =======================================================================
788 :
789 : /*
790 : * GtkSalMenuItem
791 : */
792 :
793 0 : GtkSalMenuItem::GtkSalMenuItem( const SalItemParams* pItemData ) :
794 : mnId( pItemData->nId ),
795 : mnType( pItemData->eType ),
796 : mbVisible( sal_True ),
797 : mpVCLMenu( pItemData->pMenu ),
798 : mpParentMenu( NULL ),
799 0 : mpSubMenu( NULL )
800 : {
801 0 : }
802 :
803 0 : GtkSalMenuItem::~GtkSalMenuItem()
804 : {
805 0 : }
806 :
807 : #endif
808 :
809 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|