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