1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the S60 port of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qmenu.h"
43 #include "qapplication.h"
44 #include "qevent.h"
45 #include "qstyle.h"
46 #include "qdebug.h"
47 #include "qwidgetaction.h"
48 #include <private/qapplication_p.h>
49 #include <private/qmenu_p.h>
50 #include <private/qmenubar_p.h>
51 #include <private/qt_s60_p.h>
52 #include <QtCore/qlibrary.h>
53 
54 #ifdef Q_WS_S60
55 #include <eikmenub.h>
56 #include <eikmenup.h>
57 #include <eikaufty.h>
58 #include <eikbtgpc.h>
59 #include <avkon.rsg>
60 #endif
61 
62 #if !defined(QT_NO_MENUBAR) && defined(Q_WS_S60)
63 
64 QT_BEGIN_NAMESPACE
65 
66 typedef QMultiHash<QWidget *, QMenuBarPrivate *> MenuBarHash;
67 Q_GLOBAL_STATIC(MenuBarHash, menubars)
68 
69 struct SymbianMenuItem
70 {
71     int id;
72     CEikMenuPaneItem::SData menuItemData;
73     QList<SymbianMenuItem*> children;
74     QAction* action;
75 };
76 
77 Q_GLOBAL_STATIC_WITH_ARGS(QAction, contextAction, (0))
78 
79 static QList<SymbianMenuItem*> symbianMenus;
80 static QList<QMenuBar*> nativeMenuBars;
81 static uint qt_symbian_menu_static_cmd_id = QT_SYMBIAN_FIRST_MENU_ITEM;
82 static QPointer<QWidget> widgetWithContextMenu;
83 static QList<QAction*> contextMenuActionList;
84 static QWidget* actionMenu = NULL;
85 static int contexMenuCommand=0;
86 
menuExists()87 bool menuExists()
88 {
89     QWidget *w = qApp->activeWindow();
90     QMenuBarPrivate *mb = menubars()->value(w);
91     if ((!mb) && !menubars()->count())
92         return false;
93     return true;
94 }
95 
hasContextMenu(QWidget * widget)96 static bool hasContextMenu(QWidget* widget)
97 {
98     if (!widget)
99         return false;
100     const Qt::ContextMenuPolicy policy = widget->contextMenuPolicy();
101     if (policy != Qt::NoContextMenu && policy != Qt::PreventContextMenu ) {
102         return true;
103     }
104     return false;
105 }
106 
qt_symbian_find_menu(int id,const QList<SymbianMenuItem * > & parent)107 static SymbianMenuItem* qt_symbian_find_menu(int id, const QList<SymbianMenuItem*> &parent)
108 {
109     int index=0;
110     while (index < parent.count()) {
111         SymbianMenuItem* temp = parent[index];
112         if (temp->menuItemData.iCascadeId == id)
113            return temp;
114         else if (temp->menuItemData.iCascadeId != 0) {
115             SymbianMenuItem* result = qt_symbian_find_menu( id, temp->children);
116             if (result)
117                 return result;
118         }
119         index++;
120     }
121     return 0;
122 }
123 
qt_symbian_find_menu_item(int id,const QList<SymbianMenuItem * > & parent)124 static SymbianMenuItem* qt_symbian_find_menu_item(int id, const QList<SymbianMenuItem*> &parent)
125 {
126     int index=0;
127     while (index < parent.count()) {
128         SymbianMenuItem* temp = parent[index];
129         if (temp->menuItemData.iCascadeId != 0) {
130             SymbianMenuItem* result = qt_symbian_find_menu_item( id, temp->children);
131             if (result)
132                 return result;
133         }
134         else if (temp->menuItemData.iCommandId == id)
135             return temp;
136         index++;
137 
138     }
139     return 0;
140 }
141 
qt_symbian_insert_action(QSymbianMenuAction * action,QList<SymbianMenuItem * > * parent)142 static void qt_symbian_insert_action(QSymbianMenuAction* action, QList<SymbianMenuItem*>* parent)
143 {
144     if (action->action->isVisible()) {
145         if (action->action->isSeparator())
146             return;
147 
148         Q_ASSERT_X(action->command <= QT_SYMBIAN_LAST_MENU_ITEM, "qt_symbian_insert_action",
149                 "Too many menu actions");
150 
151         const int underlineShortCut = QApplication::style()->styleHint(QStyle::SH_UnderlineShortcut);
152         QString actionText;
153         if (underlineShortCut)
154             actionText = action->action->text().left(CEikMenuPaneItem::SData::ENominalTextLength);
155         else
156             actionText = action->action->iconText().left(CEikMenuPaneItem::SData::ENominalTextLength);
157         TPtrC menuItemText = qt_QString2TPtrC(actionText);
158         if (action->action->menu()) {
159             SymbianMenuItem* menuItem = new SymbianMenuItem();
160             menuItem->menuItemData.iCascadeId = action->command;
161             menuItem->menuItemData.iCommandId = action->command;
162             menuItem->menuItemData.iFlags = 0;
163             menuItem->menuItemData.iText = menuItemText;
164             menuItem->action = action->action;
165             if (action->action->menu()->actions().size() == 0 || !action->action->isEnabled() )
166                 menuItem->menuItemData.iFlags |= EEikMenuItemDimmed;
167             parent->append(menuItem);
168 
169             if (action->action->menu()->actions().size() > 0) {
170                 for (int c2= 0; c2 < action->action->menu()->actions().size(); ++c2) {
171                     QScopedPointer<QSymbianMenuAction> symbianAction2(new QSymbianMenuAction);
172                     symbianAction2->action = action->action->menu()->actions().at(c2);
173                     QMenu * menu = symbianAction2->action->menu();
174                     symbianAction2->command = qt_symbian_menu_static_cmd_id++;
175                     qt_symbian_insert_action(symbianAction2.data(), &(menuItem->children));
176                 }
177             }
178 
179         } else {
180             SymbianMenuItem* menuItem = new SymbianMenuItem();
181             menuItem->menuItemData.iCascadeId = 0;
182             menuItem->menuItemData.iCommandId = action->command;
183             menuItem->menuItemData.iFlags = 0;
184             menuItem->menuItemData.iText = menuItemText;
185             menuItem->action = action->action;
186             if (!action->action->isEnabled()){
187                 menuItem->menuItemData.iFlags += EEikMenuItemDimmed;
188             }
189 
190             if (action->action->isCheckable()) {
191                 if (action->action->isChecked())
192                     menuItem->menuItemData.iFlags += EEikMenuItemCheckBox | EEikMenuItemSymbolOn;
193                 else
194                     menuItem->menuItemData.iFlags += EEikMenuItemCheckBox;
195             }
196             parent->append(menuItem);
197         }
198     }
199 }
200 
deleteAll(QList<SymbianMenuItem * > * items)201 void deleteAll(QList<SymbianMenuItem*> *items)
202 {
203     while (!items->isEmpty()) {
204         SymbianMenuItem* temp = items->takeFirst();
205         deleteAll(&temp->children);
206         delete temp;
207     }
208 }
209 
rebuildMenu()210 static void rebuildMenu()
211 {
212     widgetWithContextMenu = 0;
213     QMenuBarPrivate *mb = 0;
214     QWidget *w = qApp->activeWindow();
215     QWidget* focusWidget = QApplication::focusWidget();
216     if (focusWidget) {
217         if (hasContextMenu(focusWidget))
218             widgetWithContextMenu = focusWidget;
219     }
220 
221     if (w) {
222         mb = menubars()->value(w);
223         qt_symbian_menu_static_cmd_id = QT_SYMBIAN_FIRST_MENU_ITEM;
224         deleteAll( &symbianMenus );
225         if (!mb)
226             return;
227         mb->symbian_menubar->rebuild();
228     }
229 }
230 
231 #ifdef Q_WS_S60
qt_symbian_next_menu_from_action(QWidget * actionContainer)232 void qt_symbian_next_menu_from_action(QWidget *actionContainer)
233 {
234     actionMenu = actionContainer;
235 }
236 
qt_symbian_show_toplevel(CEikMenuPane * menuPane)237 void qt_symbian_show_toplevel( CEikMenuPane* menuPane)
238 {
239     if (actionMenu) {
240         QMenuBarPrivate *mb = 0;
241         mb = menubars()->value(actionMenu);
242         qt_symbian_menu_static_cmd_id = QT_SYMBIAN_FIRST_MENU_ITEM;
243         deleteAll( &symbianMenus );
244         Q_ASSERT(mb);
245         mb->symbian_menubar->rebuild();
246         for (int i = 0; i < symbianMenus.count(); ++i)
247             QT_TRAP_THROWING(menuPane->AddMenuItemL(symbianMenus.at(i)->menuItemData));
248         actionMenu = NULL;
249         return;
250     }
251 
252     if (!menuExists())
253         return;
254     rebuildMenu();
255     for (int i = 0; i < symbianMenus.count(); ++i)
256         QT_TRAP_THROWING(menuPane->AddMenuItemL(symbianMenus.at(i)->menuItemData));
257 }
258 
qt_symbian_show_submenu(CEikMenuPane * menuPane,int id)259 void qt_symbian_show_submenu( CEikMenuPane* menuPane, int id)
260 {
261     SymbianMenuItem* menu = qt_symbian_find_menu(id, symbianMenus);
262     if (menu) {
263         // Normally first AddMenuItemL call for menuPane will create the item array.
264         // However if we don't have any items, we still need the item array. Otherwise
265         // menupane will crash. That's why we create item array here manually, and
266         // AddMenuItemL will then use the existing array.
267         CEikMenuPane::CItemArray* itemArray = new CEikMenuPane::CItemArray;
268         Q_CHECK_PTR(itemArray);
269         menuPane->SetItemArray(itemArray);
270         menuPane->SetItemArrayOwnedExternally(EFalse);
271 
272         for (int i = 0; i < menu->children.count(); ++i)
273             QT_TRAP_THROWING(menuPane->AddMenuItemL(menu->children.at(i)->menuItemData));
274     }
275 }
276 #endif // Q_WS_S60
277 
symbianCommands(int command)278 int QMenuBarPrivate::symbianCommands(int command)
279 {
280     int ret = 0;
281 
282     if (command == contexMenuCommand && !widgetWithContextMenu.isNull()) {
283         QContextMenuEvent* event = new QContextMenuEvent(QContextMenuEvent::Keyboard, QPoint(0,0));
284         QCoreApplication::postEvent(widgetWithContextMenu, event);
285         ret = 1;
286     }
287 
288     int size = nativeMenuBars.size();
289     for (int i = 0; i < nativeMenuBars.size(); ++i) {
290         SymbianMenuItem* menu = qt_symbian_find_menu_item(command, symbianMenus);
291         if (!menu)
292             continue;
293 
294         emit nativeMenuBars.at(i)->triggered(menu->action);
295         menu->action->activate(QAction::Trigger);
296         ret = 1;
297         break;
298     }
299 
300     return ret;
301 }
302 
symbianCreateMenuBar(QWidget * parent)303 void QMenuBarPrivate::symbianCreateMenuBar(QWidget *parent)
304 {
305     Q_Q(QMenuBar);
306     if (parent) {
307         if(parent->isWindow()) {
308             menubars()->insert(q->window(), this);
309             symbian_menubar = new QSymbianMenuBarPrivate(this);
310             nativeMenuBars.append(q);
311         } else {
312             menubars()->insert(q->parentWidget(), this);
313             symbian_menubar = new QSymbianMenuBarPrivate(this);
314             nativeMenuBars.append(q);
315         }
316     }
317 }
318 
symbianDestroyMenuBar()319 void QMenuBarPrivate::symbianDestroyMenuBar()
320 {
321     Q_Q(QMenuBar);
322     int index = nativeMenuBars.indexOf(q);
323     nativeMenuBars.removeAt(index);
324     menubars()->remove(q->window(), this);
325     menubars()->remove(q->parentWidget(), this);
326     rebuildMenu();
327     if (symbian_menubar)
328         delete symbian_menubar;
329     symbian_menubar = 0;
330 }
331 
reparentMenuBar(QWidget * oldParent,QWidget * newParent)332 void QMenuBarPrivate::reparentMenuBar(QWidget *oldParent, QWidget *newParent)
333 {
334     if (menubars()->contains(oldParent)) {
335         QMenuBarPrivate *object = menubars()->take(oldParent);
336         menubars()->insert(newParent, object);
337     }
338 }
339 
QSymbianMenuBarPrivate(QMenuBarPrivate * menubar)340 QMenuBarPrivate::QSymbianMenuBarPrivate::QSymbianMenuBarPrivate(QMenuBarPrivate *menubar)
341 {
342     d = menubar;
343 }
344 
~QSymbianMenuBarPrivate()345 QMenuBarPrivate::QSymbianMenuBarPrivate::~QSymbianMenuBarPrivate()
346 {
347     qt_symbian_menu_static_cmd_id = QT_SYMBIAN_FIRST_MENU_ITEM;
348     deleteAll( &symbianMenus );
349     symbianMenus.clear();
350     d = 0;
351     rebuild();
352 }
353 
QSymbianMenuPrivate()354 QMenuPrivate::QSymbianMenuPrivate::QSymbianMenuPrivate()
355 {
356 }
357 
~QSymbianMenuPrivate()358 QMenuPrivate::QSymbianMenuPrivate::~QSymbianMenuPrivate()
359 {
360     qDeleteAll(actionItems);
361 }
362 
addAction(QAction * a,QSymbianMenuAction * before)363 void QMenuPrivate::QSymbianMenuPrivate::addAction(QAction *a, QSymbianMenuAction *before)
364 {
365     QSymbianMenuAction *action = new QSymbianMenuAction;
366     action->action = a;
367     action->command = qt_symbian_menu_static_cmd_id++;
368     addAction(action, before);
369 }
370 
addAction(QSymbianMenuAction * action,QSymbianMenuAction * before)371 void QMenuPrivate::QSymbianMenuPrivate::addAction(QSymbianMenuAction *action, QSymbianMenuAction *before)
372 {
373     if (!action)
374         return;
375     int before_index = actionItems.indexOf(before);
376     if (before_index < 0) {
377         before = 0;
378         before_index = actionItems.size();
379     }
380     actionItems.insert(before_index, action);
381 }
382 
383 
syncAction(QSymbianMenuAction *)384 void QMenuPrivate::QSymbianMenuPrivate::syncAction(QSymbianMenuAction *)
385 {
386     rebuild();
387 }
388 
removeAction(QSymbianMenuAction * action)389 void QMenuPrivate::QSymbianMenuPrivate::removeAction(QSymbianMenuAction *action)
390 {
391     actionItems.removeAll(action);
392     delete action;
393     action = 0;
394     rebuild();
395 }
396 
rebuild(bool)397 void QMenuPrivate::QSymbianMenuPrivate::rebuild(bool)
398 {
399 }
400 
addAction(QAction * a,QAction * before)401 void QMenuBarPrivate::QSymbianMenuBarPrivate::addAction(QAction *a, QAction *before)
402 {
403     QSymbianMenuAction *action = new QSymbianMenuAction;
404     action->action = a;
405     action->command = qt_symbian_menu_static_cmd_id++;
406     addAction(action, findAction(before));
407 }
408 
addAction(QSymbianMenuAction * action,QSymbianMenuAction * before)409 void QMenuBarPrivate::QSymbianMenuBarPrivate::addAction(QSymbianMenuAction *action, QSymbianMenuAction *before)
410 {
411     if (!action)
412         return;
413     int before_index = actionItems.indexOf(before);
414     if (before_index < 0) {
415         before = 0;
416         before_index = actionItems.size();
417     }
418     actionItems.insert(before_index, action);
419 }
420 
syncAction(QSymbianMenuAction *)421 void QMenuBarPrivate::QSymbianMenuBarPrivate::syncAction(QSymbianMenuAction*)
422 {
423     rebuild();
424 }
425 
removeAction(QSymbianMenuAction * action)426 void QMenuBarPrivate::QSymbianMenuBarPrivate::removeAction(QSymbianMenuAction *action)
427 {
428     actionItems.removeAll(action);
429     delete action;
430     rebuild();
431 }
432 
insertNativeMenuItems(const QList<QAction * > & actions)433 void QMenuBarPrivate::QSymbianMenuBarPrivate::insertNativeMenuItems(const QList<QAction*> &actions)
434 {
435     for (int i = 0; i <actions.size(); ++i) {
436         QScopedPointer<QSymbianMenuAction> symbianActionTopLevel(new QSymbianMenuAction);
437         symbianActionTopLevel->action = actions.at(i);
438         symbianActionTopLevel->parent = 0;
439         symbianActionTopLevel->command = qt_symbian_menu_static_cmd_id++;
440         qt_symbian_insert_action(symbianActionTopLevel.data(), &symbianMenus);
441     }
442 }
443 
444 
445 
rebuild()446 void QMenuBarPrivate::QSymbianMenuBarPrivate::rebuild()
447 {
448     contexMenuCommand = 0;
449     qt_symbian_menu_static_cmd_id = QT_SYMBIAN_FIRST_MENU_ITEM;
450     deleteAll( &symbianMenus );
451     if (d)
452         insertNativeMenuItems(d->actions);
453 
454     contextMenuActionList.clear();
455     if (widgetWithContextMenu) {
456         contexMenuCommand = qt_symbian_menu_static_cmd_id; // Increased inside insertNativeMenuItems
457         contextAction()->setText(QMenuBar::tr("Actions"));
458         contextMenuActionList.append(contextAction());
459         insertNativeMenuItems(contextMenuActionList);
460     }
461 }
462 QT_END_NAMESPACE
463 
464 #endif //QT_NO_MENUBAR
465