|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (developer.feedback@nokia.com) |
|
6 ** |
|
7 ** This file is part of the HbCore module of the UI Extensions for Mobile. |
|
8 ** |
|
9 ** GNU Lesser General Public License Usage |
|
10 ** This file may be used under the terms of the GNU Lesser General Public |
|
11 ** License version 2.1 as published by the Free Software Foundation and |
|
12 ** appearing in the file LICENSE.LGPL included in the packaging of this file. |
|
13 ** Please review the following information to ensure the GNU Lesser General |
|
14 ** Public License version 2.1 requirements will be met: |
|
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
16 ** |
|
17 ** In addition, as a special exception, Nokia gives you certain additional |
|
18 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
20 ** |
|
21 ** If you have questions regarding the use of this file, please contact |
|
22 ** Nokia at developer.feedback@nokia.com. |
|
23 ** |
|
24 ****************************************************************************/ |
|
25 |
|
26 #include "hbmenu.h" |
|
27 #include "hbmenu_p.h" |
|
28 #include "hbmenuitem_p.h" |
|
29 #include "hbaction.h" |
|
30 #include <hbwidgetfeedback.h> |
|
31 #include "hbinstance.h" |
|
32 #include "hbmenucontainer_p.h" |
|
33 #ifdef HB_EFFECTS |
|
34 #include "hbeffect.h" |
|
35 #include "hbeffectinternal_p.h" |
|
36 bool HbMenuPrivate::menuEffectsLoaded = false; |
|
37 #endif |
|
38 |
|
39 #include <QPointer> |
|
40 |
|
41 Q_DECLARE_METATYPE (QAction*)// krazy:exclude=qclasses |
|
42 |
|
43 HbMenuPrivate::HbMenuPrivate(): |
|
44 HbPopupPrivate(), |
|
45 menuItemView(0), |
|
46 subMenuAction(0), |
|
47 activeSubMenu(0), |
|
48 resultAction(0), |
|
49 actionTriggered(false), |
|
50 menuType(HbMenu::ContextMenu), |
|
51 mSubMenuItem(0), |
|
52 mRightMargin(0.0), |
|
53 mDownMargin(0.0), |
|
54 delayMenuConstruction(true), |
|
55 receiverToDisconnectOnClose(0) |
|
56 { |
|
57 } |
|
58 |
|
59 HbMenuPrivate::~HbMenuPrivate() |
|
60 { |
|
61 } |
|
62 |
|
63 void HbMenuPrivate::init() |
|
64 { |
|
65 Q_Q(HbMenu); |
|
66 |
|
67 subMenuAction = new HbAction(q); |
|
68 subMenuAction->setMenu(q); |
|
69 q->setTimeout(HbPopup::ContextMenuTimeout); |
|
70 |
|
71 q->setBackgroundFaded(false); |
|
72 q->setFlag(QGraphicsItem::ItemClipsChildrenToShape); |
|
73 } |
|
74 |
|
75 void HbMenuPrivate::addPopupEffects() |
|
76 { |
|
77 #ifdef HB_EFFECTS |
|
78 effectType = "HB_MENU"; |
|
79 hasEffects = menuEffectsLoaded; |
|
80 if (menuEffectsLoaded) |
|
81 return; |
|
82 menuEffectsLoaded = true; |
|
83 hasEffects = HbEffectInternal::add("HB_MENU", "menu_appear", "appear"); |
|
84 if (hasEffects) { |
|
85 //We load the disappear effect only if appear effect was also loaded |
|
86 hasEffects = HbEffectInternal::add("HB_MENU", "menu_disappear", "disappear"); |
|
87 |
|
88 if (hasEffects) { |
|
89 hasEffects = HbEffectInternal::add("HB_menuitem", "menuitem_press", "clicked"); |
|
90 } |
|
91 if (hasEffects ) { |
|
92 hasEffects = HbEffectInternal::add("HB_POPUP", |
|
93 "dialog_rotate", |
|
94 "orientationswitch"); |
|
95 } |
|
96 } |
|
97 #endif |
|
98 } |
|
99 |
|
100 void HbMenuPrivate::_q_triggerAction(HbMenuItem *currentItem) |
|
101 { |
|
102 Q_Q(HbMenu); |
|
103 if (currentItem && currentItem->action()){ |
|
104 HbWidgetFeedback::triggered(currentItem, Hb::InstantClicked); |
|
105 #ifdef HB_EFFECTS |
|
106 if (hasEffects) { |
|
107 HbEffect::start(currentItem, "HB_menuitem", "clicked"); |
|
108 } |
|
109 #endif |
|
110 HbAction *hbAction = qobject_cast<HbAction *>(currentItem->action()); |
|
111 if (hbAction && hbAction->menu()) { |
|
112 hbAction->trigger(); |
|
113 stopTimeout(); |
|
114 openSubmenu(currentItem); |
|
115 } else { |
|
116 q->close(); |
|
117 |
|
118 resultAction = hbAction; |
|
119 |
|
120 if (!actionTriggered) { // prevent duplicate events |
|
121 currentItem->action()->trigger(); |
|
122 } |
|
123 actionTriggered = true; |
|
124 } |
|
125 } |
|
126 } |
|
127 |
|
128 void HbMenuPrivate::createMenuView() |
|
129 { |
|
130 Q_Q(HbMenu); |
|
131 if (!menuItemView && q->actions().count()){ |
|
132 menuItemView = new HbMenuListView(q, q); |
|
133 HbStyle::setItemName(menuItemView, "content"); |
|
134 /* This is for qt versions 4.5,which had the clipping problem. |
|
135 FOR http://www.qtsoftware.com/developer/task-tracker/index_html?id=257232&method=entry |
|
136 see also HbMenu constructor */ |
|
137 #if QT_VERSION < 0x040600 |
|
138 menuItemView->setFlag( QGraphicsItem::ItemClipsChildrenToShape, false ); |
|
139 #endif |
|
140 //This optimises case of options menu which otherwise updates its primitives twice. |
|
141 if (menuType == HbMenu::OptionsMenu) |
|
142 q->setFrameType(HbPopup::Strong); |
|
143 else |
|
144 q->setFrameType(HbPopup::Weak); |
|
145 if (polished)//This check can be removed once base class repolish is fixed. |
|
146 q->repolish(); |
|
147 } |
|
148 } |
|
149 |
|
150 void HbMenuPrivate::delayedLayout() |
|
151 { |
|
152 createMenuView(); |
|
153 if(menuItemView) |
|
154 menuItemView->doDelayedLayout(); |
|
155 delayMenuConstruction = false; |
|
156 } |
|
157 |
|
158 void HbMenuPrivate::changeToOptionsMenu() |
|
159 { |
|
160 menuType = HbMenu::OptionsMenu; |
|
161 } |
|
162 |
|
163 HbMenuItem *HbMenuPrivate::subMenuItem() |
|
164 { |
|
165 return mSubMenuItem; |
|
166 } |
|
167 |
|
168 void HbMenuPrivate::setSubMenuItem(HbMenuItem *menuItem) |
|
169 { |
|
170 mSubMenuItem = menuItem; |
|
171 } |
|
172 |
|
173 void HbMenuPrivate::_q_onActionTriggered() |
|
174 { |
|
175 Q_Q(HbMenu); |
|
176 HbAction *action = qobject_cast<HbAction *>(q->sender()); |
|
177 if (action && !action->menu() ) { // do not trigger from opening submenu |
|
178 emit q->triggered(action); |
|
179 } |
|
180 } |
|
181 |
|
182 void HbMenuPrivate::_q_subMenuItemTriggered(HbAction *action) |
|
183 { |
|
184 Q_Q(HbMenu); |
|
185 |
|
186 // do not close the menu tree if the triggered action is |
|
187 // submenu item |
|
188 if (!action->menu()) { |
|
189 q->close(); |
|
190 } else { |
|
191 stopTimeout(); |
|
192 } |
|
193 } |
|
194 |
|
195 void HbMenuPrivate::actionAdded(QActionEvent *actionEvent) |
|
196 { |
|
197 if (delayMenuConstruction) |
|
198 return; |
|
199 if (actionEvent->action()->isVisible()){ |
|
200 Q_Q(HbMenu); |
|
201 createMenuView(); |
|
202 QObject::connect(actionEvent->action(), SIGNAL(triggered()), q, SLOT(_q_onActionTriggered())); |
|
203 menuItemView->addActionItem(actionEvent->action()); |
|
204 } |
|
205 } |
|
206 |
|
207 void HbMenuPrivate::actionRemoved(QActionEvent *actionEvent) |
|
208 { |
|
209 if (delayMenuConstruction) |
|
210 return; |
|
211 Q_Q(HbMenu); |
|
212 QObject::disconnect(actionEvent->action(), 0, q, 0); |
|
213 if (menuItemView) |
|
214 menuItemView->removeActionItem(actionEvent->action()); |
|
215 } |
|
216 |
|
217 void HbMenuPrivate::actionChanged(QActionEvent *actionEvent) |
|
218 { |
|
219 if (delayMenuConstruction) |
|
220 return; |
|
221 if (menuItemView) |
|
222 menuItemView->updateActionItem(actionEvent->action()); |
|
223 } |
|
224 |
|
225 /* |
|
226 Returns current focusable action based on current row of the menuItemView or index if specified. |
|
227 If there is no focusable action it returns 0. |
|
228 Also returns the active item representing the active action or 0. |
|
229 */ |
|
230 HbAction *HbMenuPrivate::activeAction(HbMenuItem *&activeItem) const |
|
231 { |
|
232 if(!menuItemView) |
|
233 return 0; |
|
234 HbAction *action = 0; |
|
235 HbMenuItem *currentItem = menuItemView->currentItem(); |
|
236 if(currentItem && currentItem->action() && currentItem->action()->isVisible() && |
|
237 currentItem->action()->isEnabled() && !currentItem->action()->isSeparator()) { |
|
238 action = static_cast<HbAction*>(currentItem->action()); |
|
239 activeItem = currentItem; |
|
240 } |
|
241 return action; |
|
242 } |
|
243 |
|
244 /* |
|
245 Convenience overload |
|
246 */ |
|
247 HbAction *HbMenuPrivate::activeAction() const |
|
248 { |
|
249 HbMenuItem* activeItem = 0; |
|
250 return activeAction(activeItem); |
|
251 } |
|
252 |
|
253 /* |
|
254 Opens a submenu for activeItem. If activeItem is 0 it uses activeAction() to determine active item |
|
255 and opens submenu for it if active action has submenu. |
|
256 */ |
|
257 void HbMenuPrivate::openSubmenu(HbMenuItem *activeItem) |
|
258 { |
|
259 Q_Q(HbMenu); |
|
260 |
|
261 if (!activeItem) { |
|
262 activeAction(activeItem); |
|
263 } |
|
264 |
|
265 if (activeItem && activeItem->action() && activeItem->action()->isEnabled()) { |
|
266 HbAction *hbAction = qobject_cast<HbAction *>(activeItem->action()); |
|
267 if (!hbAction) |
|
268 return; |
|
269 HbMenu *subMenu = hbAction->menu(); |
|
270 if ( subMenu ) { |
|
271 subMenu->setLayoutDirection(q->layoutDirection()); |
|
272 |
|
273 activeSubMenu = subMenu; |
|
274 subMenu->setTimeout(timeout); |
|
275 QObject::disconnect(subMenu, SIGNAL(aboutToClose()), q, SLOT(_q_subMenuTimedOut())); |
|
276 QObject::connect(subMenu, SIGNAL(aboutToClose()), q, SLOT(_q_subMenuTimedOut())); |
|
277 QObject::connect(subMenu, SIGNAL(triggered(HbAction*)), q, SLOT(_q_subMenuItemTriggered(HbAction*))); |
|
278 subMenu->show(); |
|
279 |
|
280 // Reset the active submenu so that mouse event handling works. |
|
281 // huh ? activeSubMenu = 0; |
|
282 } |
|
283 } |
|
284 } |
|
285 |
|
286 void HbMenuPrivate::_q_subMenuTimedOut() |
|
287 { |
|
288 Q_Q(HbMenu); |
|
289 if( menuTimedOut (activeSubMenu) ) { |
|
290 if ( activeSubMenu ) { |
|
291 activeSubMenu->disconnect(); |
|
292 } |
|
293 q->close(); |
|
294 } |
|
295 } |
|
296 |
|
297 bool HbMenuPrivate::menuTimedOut(HbMenu* menu) |
|
298 { |
|
299 return (menu && menu->timeout() > 0 && HbMenuPrivate::d_ptr(menu)->timedOut); |
|
300 } |
|
301 |
|
302 void HbMenuPrivate::closeSubmenu() |
|
303 { |
|
304 // Check whether this is a submenu. |
|
305 if (activeSubMenu) { |
|
306 activeSubMenu->close(); |
|
307 } |
|
308 } |
|
309 |
|
310 void HbMenuPrivate::setSubMenuPosition() |
|
311 { |
|
312 Q_Q(HbMenu); |
|
313 if (mSubMenuItem) { |
|
314 qreal upperEdge = mSubMenuItem->scenePos().y() + mSubMenuItem->size().height() * 2 / 3; |
|
315 QSizeF windowSize = QSizeF(0,0); |
|
316 if (q->mainWindow()) { |
|
317 QGraphicsWidget *viewPortItem = q->mainWindow()->element(HbMainWindow::ViewportItem); |
|
318 if (viewPortItem) { |
|
319 windowSize = viewPortItem->size(); |
|
320 } |
|
321 } |
|
322 if (windowSize.height() - mDownMargin - q->preferredHeight() < upperEdge) { |
|
323 upperEdge = windowSize.height() - mDownMargin - q->preferredHeight(); |
|
324 } |
|
325 if (q->layoutDirection() == Qt::LeftToRight) { |
|
326 qreal leftEdge = mSubMenuItem->scenePos().x() + |
|
327 mSubMenuItem->size().width() - mRightMargin; |
|
328 if ((windowSize.width() - q->size().width()) < leftEdge) { |
|
329 leftEdge = mSubMenuItem->scenePos().x() + |
|
330 mSubMenuItem->size().width() + |
|
331 mRightMargin - q->size().width(); |
|
332 } |
|
333 q->setPreferredPos(QPointF(leftEdge, upperEdge)); |
|
334 } else { |
|
335 qreal rightEdge = mSubMenuItem->scenePos().x() + mRightMargin; |
|
336 if ((rightEdge - q->size().width()) < 0) { |
|
337 rightEdge = mSubMenuItem->scenePos().x() - mRightMargin + |
|
338 q->size().width(); |
|
339 } |
|
340 q->setPreferredPos(QPointF(rightEdge, upperEdge), HbPopup::TopRightCorner); |
|
341 } |
|
342 } |
|
343 } |
|
344 |
|
345 /*! |
|
346 @stable |
|
347 @hbcore |
|
348 \class HbMenu |
|
349 \brief HbMenu is a menu widget for use in HbView. |
|
350 |
|
351 \image html hbmenu.png A menu with checkable items and a sub-menu. |
|
352 |
|
353 Use an HbMenu to show a list of options. There are two main types of menus: |
|
354 |
|
355 - The view options menu |
|
356 - Context menus (popup menus) |
|
357 |
|
358 There is one view options menu for each view. |
|
359 It is shown by tapping the title bar. You can access the view options menu by calling |
|
360 HbView::menu() which returns a pointer to a new empty menu if one does not already exist. |
|
361 |
|
362 You can create any number of context menus. |
|
363 Context menus are usually invoked by a user action, such as tapping a widget. |
|
364 |
|
365 A menu contains a list of items. You can add three kinds of items to a menu: |
|
366 |
|
367 - Actions |
|
368 - Sub-menus |
|
369 - Separators |
|
370 |
|
371 An action is an object of class HbAction that performs an action when it is triggered. |
|
372 Use addAction() to add an action to a menu. Actions can be checkable (QAction::setCheckable). |
|
373 Use clearActions() to clear all actions from a menu. |
|
374 Use removeAction() to remove individual actions from a menu. |
|
375 |
|
376 A sub-menu is a menu that is nested within another menu. Use addMenu() or insertMenu() to add a sub-menu to a menu. |
|
377 Sub-menus can be nested within sub-menus. |
|
378 |
|
379 Separators group related items in a menu. |
|
380 Use addSeparator() or insertSeparator() to create and add a separator to a menu. |
|
381 |
|
382 \image html hbmenu.png A menu with checkable actions and a sub-menu. |
|
383 |
|
384 After you add an action to your menu, you specify a receiver object and its slot (you can also |
|
385 add an action and specify a receiver slot at the same time). |
|
386 The receiver is notifed when the action is triggered (QAction::triggered()). |
|
387 HbMenu also has a triggered() menu signal, which signals which HbAction was triggered in the menu. |
|
388 |
|
389 Context menu example: |
|
390 |
|
391 A menu and a few actions are created. The triggered() signal of the menu is connected to |
|
392 the mute() function of the enclosing class (implementation not shown). |
|
393 The exec() function shows the menu. |
|
394 |
|
395 User needs to connect to "longPress" signal and implement corresponding slot. This enables |
|
396 longpress events to be received from list. |
|
397 |
|
398 \dontinclude decoratorlistdemo/contentwidget.cpp |
|
399 \skip // Create new menu |
|
400 \until ( coords ); |
|
401 |
|
402 \sa HbDialog, HbView |
|
403 */ |
|
404 |
|
405 /*! |
|
406 \property HbMenu::menuType |
|
407 \brief |
|
408 */ |
|
409 |
|
410 /*! |
|
411 \fn void HbMenu::triggered(HbAction *action) |
|
412 |
|
413 This signal is emitted when one of the action items is selected. |
|
414 \param action the action that was triggered in the menu. |
|
415 */ |
|
416 |
|
417 /*! |
|
418 \enum HbMenu::MenuType |
|
419 |
|
420 This enum describes different types of HbMenu. |
|
421 */ |
|
422 /*! |
|
423 \var HbMenu::ContextMenu |
|
424 |
|
425 ContextMenu is a menu which position is set by user. |
|
426 */ |
|
427 /*! |
|
428 \var HbMenu::OptionMenu |
|
429 |
|
430 OptionMenu is set by HbView. Its position cannot be changed. |
|
431 */ |
|
432 /*! |
|
433 \var HbMenu::SubMenu |
|
434 |
|
435 Menu becomes SubMenu when it is added to another menu. |
|
436 */ |
|
437 |
|
438 /*! |
|
439 Constructs a menu with \a parent graphics item. |
|
440 |
|
441 \param parent is the parent graphics item. |
|
442 */ |
|
443 HbMenu::HbMenu(QGraphicsItem *parent) : |
|
444 HbPopup(*new HbMenuPrivate, parent) |
|
445 { |
|
446 Q_D(HbMenu); |
|
447 d->q_ptr = this; |
|
448 d->init(); |
|
449 /* This is for qt versions 4.5,which had the clipping problem. |
|
450 FOR http://www.qtsoftware.com/developer/task-tracker/index_html?id=257232&method=entry |
|
451 */ |
|
452 #if QT_VERSION < 0x040600 |
|
453 setFlag( QGraphicsItem::ItemClipsChildrenToShape, true ); |
|
454 #endif |
|
455 } |
|
456 |
|
457 /*! |
|
458 Constructs a menu with \a title and \a parent graphics item. |
|
459 \param title is the menu title. |
|
460 \param parent is the parent graphics item. |
|
461 */ |
|
462 HbMenu::HbMenu(const QString &title, QGraphicsItem *parent) : |
|
463 HbPopup(*new HbMenuPrivate, parent) |
|
464 { |
|
465 Q_D(HbMenu); |
|
466 d->q_ptr = this; |
|
467 d->init(); |
|
468 setTitle(title); |
|
469 } |
|
470 |
|
471 HbMenu::HbMenu(HbMenuPrivate &dd, QGraphicsItem *parent) : |
|
472 HbPopup(dd, parent) |
|
473 { |
|
474 Q_D(HbMenu); |
|
475 d->q_ptr = this; |
|
476 d->init(); |
|
477 } |
|
478 |
|
479 HbMenu::~HbMenu() |
|
480 { |
|
481 if (!scene() || !scene()->property("destructed").isValid()) { |
|
482 foreach (QAction *action, actions()) {// krazy:exclude=qclasses |
|
483 HbAction* hbAction = qobject_cast<HbAction *>(action); |
|
484 if (hbAction){ |
|
485 if (hbAction->menu()) { |
|
486 hbAction->menu()->deleteLater(); |
|
487 } |
|
488 hbAction->setMenu((HbMenu*)0); |
|
489 } |
|
490 } |
|
491 } |
|
492 } |
|
493 |
|
494 /*! |
|
495 \deprecated HbMenu::exec(HbAction*) |
|
496 is deprecated. Please use void HbMenu::open( QObject *receiver, const char *member ) |
|
497 or HbMenu::show() instead. |
|
498 |
|
499 Executes the menu synchronously so that given \a action |
|
500 is active. |
|
501 |
|
502 \param action is the action that is active when the menu is shown. |
|
503 |
|
504 Example usage: |
|
505 \code |
|
506 void MyGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) |
|
507 { |
|
508 HbMenu menu(); |
|
509 menu.addAction(...); |
|
510 ... |
|
511 menu.setPreferredPos(event->scenePos(), HbPopup::BottomEdgeCenter); |
|
512 menu.exec(); |
|
513 } |
|
514 \endcode |
|
515 |
|
516 \return the triggered HbAction in either the popup menu or one |
|
517 of its sub-menus, or 0 if no item was triggered (normally because |
|
518 the user closed or cancelled the menu). |
|
519 */ |
|
520 HbAction *HbMenu::exec(HbAction *action) |
|
521 { |
|
522 Q_D(HbMenu); |
|
523 if (actions().count() == 0) { |
|
524 return 0; |
|
525 } |
|
526 |
|
527 if(!action) |
|
528 action = qobject_cast<HbAction*>(actions().first()); |
|
529 |
|
530 setActiveAction(action); |
|
531 |
|
532 // Reset state variables |
|
533 d->resultAction = 0; |
|
534 d->actionTriggered = false; |
|
535 |
|
536 if (d->menuType == SubMenu && d->polished) { |
|
537 d->setSubMenuPosition(); |
|
538 } |
|
539 QPointer<HbMenu> menuAlive(this); |
|
540 HbPopup::exec(); |
|
541 |
|
542 // HbMenu can be deleted while exec |
|
543 if (menuAlive) { |
|
544 return d->resultAction; |
|
545 } else { |
|
546 return 0; |
|
547 } |
|
548 } |
|
549 |
|
550 /*! |
|
551 \deprecated HbMenu::exec(const QPointF&, HbAction*) |
|
552 is deprecated. Please use void HbMenu::open( QObject *receiver, const char *member ) |
|
553 or HbMenu::show() and setPreferredPos() instead. |
|
554 |
|
555 Executes the menu synchronously at \a pos so that given \a action |
|
556 is active. |
|
557 |
|
558 \param pos is the position at which the menu is shown. |
|
559 \param action is the action that is active when the menu is shown. |
|
560 |
|
561 Example usage: |
|
562 \code |
|
563 void MyGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) |
|
564 { |
|
565 HbMenu menu(); |
|
566 menu.addAction(...); |
|
567 ... |
|
568 menu.exec(event->scenePos()); |
|
569 } |
|
570 \endcode |
|
571 |
|
572 \return the triggered HbAction in either the popup menu or one |
|
573 of its sub-menus, or 0 if no item was triggered (normally because |
|
574 the user closed or cancelled the menu). |
|
575 */ |
|
576 HbAction *HbMenu::exec(const QPointF &pos, HbAction *action ) |
|
577 { |
|
578 Q_D(HbMenu); |
|
579 if (d->menuType == ContextMenu) { |
|
580 setPreferredPos(pos); |
|
581 } |
|
582 return exec(action); |
|
583 } |
|
584 |
|
585 void HbMenu::showEvent(QShowEvent *event) |
|
586 { |
|
587 Q_UNUSED(event); |
|
588 Q_D(HbMenu); |
|
589 d->actionTriggered = false; |
|
590 |
|
591 if (d->menuType == SubMenu && d->polished) { |
|
592 d->setSubMenuPosition(); |
|
593 } |
|
594 |
|
595 HbPopup::showEvent(event); |
|
596 } |
|
597 |
|
598 /*! |
|
599 Creates a new action with title \a text. It adds the newly created action to the menu's list of actions. |
|
600 \param text is the text for the new action. |
|
601 \return the new action. |
|
602 */ |
|
603 HbAction *HbMenu::addAction(const QString &text) |
|
604 { |
|
605 HbAction *action = new HbAction(text, this); |
|
606 addAction(action); |
|
607 return action; |
|
608 } |
|
609 |
|
610 /*! |
|
611 Creates a new action with \a text. |
|
612 The action's triggered() signal is connected to the |
|
613 \a receiver's \a member slot. The function adds the newly created |
|
614 action to the menu's list of actions. |
|
615 \return the new action. |
|
616 */ |
|
617 HbAction *HbMenu::addAction(const QString &text, const QObject *receiver, const char *member) |
|
618 { |
|
619 HbAction *action = new HbAction(text, this); |
|
620 connect(action, SIGNAL(triggered(bool)), receiver, member); |
|
621 addAction(action); |
|
622 return action; |
|
623 } |
|
624 |
|
625 /*! |
|
626 Adds \a menu as a sub-menu. |
|
627 \param menu is the menu that is added to this one. |
|
628 \return the action for the added sub-menu. |
|
629 */ |
|
630 HbAction *HbMenu::addMenu(HbMenu *menu) |
|
631 { |
|
632 return insertMenu(0, menu); |
|
633 } |
|
634 |
|
635 /*! |
|
636 Creates a new HbMenu with \a title and adds it to this menu. |
|
637 \param title is the menu title. |
|
638 \return the new menu. |
|
639 */ |
|
640 HbMenu *HbMenu::addMenu(const QString &title) |
|
641 { |
|
642 HbMenu *menu = new HbMenu(title); |
|
643 addMenu(menu); |
|
644 return menu; |
|
645 } |
|
646 |
|
647 /*! |
|
648 Inserts \a menu before action \a before. |
|
649 \param before is the action before which this new menu is inserted. |
|
650 \param menu is the menu that is inserted. |
|
651 \return the action associated with the inserted menu. |
|
652 */ |
|
653 HbAction *HbMenu::insertMenu(HbAction *before, HbMenu *menu) |
|
654 { |
|
655 QObject::connect(menu, SIGNAL(triggered(HbAction*)), this, SLOT(_q_subMenuItemTriggered(HbAction*))); |
|
656 QObject::connect(menu, SIGNAL(triggered(HbAction*)), this, SIGNAL(triggered(HbAction*))); |
|
657 |
|
658 menu->d_func()->menuType = HbMenu::SubMenu; |
|
659 insertAction(before, menu->menuAction()); |
|
660 return menu->menuAction(); |
|
661 } |
|
662 |
|
663 /*! |
|
664 \return the action associated with this menu. |
|
665 */ |
|
666 HbAction *HbMenu::menuAction() const |
|
667 { |
|
668 Q_D(const HbMenu); |
|
669 return d->subMenuAction; |
|
670 } |
|
671 |
|
672 /*! |
|
673 Creates a new separator action, which is an action that returns \c true from HbAction::isSeparator(), |
|
674 and adds it to this menu's list of actions. |
|
675 \return the new separator action |
|
676 |
|
677 \sa insertSeparator |
|
678 */ |
|
679 HbAction *HbMenu::addSeparator() |
|
680 { |
|
681 return insertSeparator(0); |
|
682 } |
|
683 |
|
684 /*! |
|
685 Inserts a new separator action and inserts it into this menu's list of actions before \a action. |
|
686 \param before is the action before which the separator is inserted. |
|
687 \return the new action. |
|
688 |
|
689 \sa addSeparator |
|
690 */ |
|
691 HbAction *HbMenu::insertSeparator(HbAction *before) |
|
692 { |
|
693 HbAction *action = new HbAction(this); |
|
694 action->setSeparator(true); |
|
695 action->setEnabled(false); |
|
696 insertAction(before, action); |
|
697 return action; |
|
698 } |
|
699 |
|
700 /*! |
|
701 \return the current active action, or 0 if no action item is currently active. |
|
702 */ |
|
703 HbAction *HbMenu::activeAction() const |
|
704 { |
|
705 Q_D(const HbMenu); |
|
706 return d->activeAction(); |
|
707 } |
|
708 |
|
709 /*! |
|
710 Sets the active action in menu. If \a action is not found from the list of |
|
711 menu actions then the current active action remains active. |
|
712 |
|
713 \sa activeAction() |
|
714 */ |
|
715 void HbMenu::setActiveAction(HbAction *action) |
|
716 { |
|
717 Q_D(HbMenu); |
|
718 if (d->menuItemView && action && action->isVisible() && !action->isSeparator()) { |
|
719 d->menuItemView->setCurrentItem(action); |
|
720 } |
|
721 } |
|
722 |
|
723 /*! |
|
724 \return \c true if the menu is empty (contains no actions) and \c false otherwise. |
|
725 |
|
726 \sa clear() |
|
727 */ |
|
728 bool HbMenu::isEmpty() const |
|
729 { |
|
730 return actions().isEmpty(); |
|
731 } |
|
732 |
|
733 /*! |
|
734 Sets the menu title. For a sub-menu, the title is the sub-menu action text. |
|
735 |
|
736 \sa title() |
|
737 */ |
|
738 void HbMenu::setTitle(const QString &title) |
|
739 { |
|
740 menuAction()->setText(title); |
|
741 } |
|
742 |
|
743 /*! |
|
744 \return the menu title. For a sub-menu, the title is the sub-menu action text. |
|
745 |
|
746 \sa setTitle() |
|
747 */ |
|
748 QString HbMenu::title() const |
|
749 { |
|
750 return menuAction()->text(); |
|
751 } |
|
752 |
|
753 /*! |
|
754 \return the menu type. By default a menu is a context menu. |
|
755 */ |
|
756 HbMenu::MenuType HbMenu::menuType() const |
|
757 { |
|
758 Q_D(const HbMenu); |
|
759 return d->menuType; |
|
760 } |
|
761 |
|
762 /*! |
|
763 \reimp |
|
764 */ |
|
765 QVariant HbMenu::itemChange( GraphicsItemChange change, const QVariant & value ) |
|
766 { |
|
767 Q_D(HbMenu); |
|
768 |
|
769 if (change == QGraphicsItem::ItemVisibleChange) { |
|
770 if (value.toBool() && d->delayMenuConstruction) { |
|
771 d->delayedLayout(); |
|
772 } |
|
773 if (value.toBool()) { |
|
774 d->resultAction = 0; |
|
775 d->actionTriggered = false; |
|
776 } |
|
777 else if (!value.toBool() && !d->menuItemView){ |
|
778 d->delayMenuConstruction = true; |
|
779 } |
|
780 } |
|
781 return HbPopup::itemChange(change,value); |
|
782 } |
|
783 |
|
784 /*! |
|
785 \reimp |
|
786 */ |
|
787 void HbMenu::keyPressEvent(QKeyEvent *event) |
|
788 { |
|
789 //TODO: check if non-touch version works with the key bindings below |
|
790 Q_D(HbMenu); |
|
791 switch( event->key() ) { |
|
792 case Qt::Key_Up: |
|
793 case Qt::Key_Down: |
|
794 break; |
|
795 case Qt::Key_Right: |
|
796 layoutDirection() == Qt::LeftToRight |
|
797 ? d->openSubmenu() |
|
798 : d->closeSubmenu(); |
|
799 break; |
|
800 case Qt::Key_Left: |
|
801 layoutDirection() == Qt::LeftToRight |
|
802 ? d->closeSubmenu() |
|
803 : d->openSubmenu(); |
|
804 break; |
|
805 case Qt::Key_Backspace: |
|
806 d->closeSubmenu(); |
|
807 break; |
|
808 case Qt::Key_Escape: |
|
809 close(); |
|
810 break; |
|
811 default: |
|
812 HbPopup::keyPressEvent( event ); |
|
813 break; |
|
814 } |
|
815 } |
|
816 |
|
817 /*! |
|
818 \reimp |
|
819 */ |
|
820 void HbMenu::keyReleaseEvent(QKeyEvent *event) |
|
821 { |
|
822 //TODO do we need this method? |
|
823 QGraphicsWidget::keyReleaseEvent( event ); |
|
824 } |
|
825 |
|
826 /*! |
|
827 \reimp |
|
828 */ |
|
829 bool HbMenu::event(QEvent *event) |
|
830 { |
|
831 Q_D(HbMenu); |
|
832 |
|
833 if(!d->inDestruction) { |
|
834 if (event->type() == QEvent::ActionAdded) { |
|
835 QActionEvent *actionEvent = static_cast<QActionEvent *>(event); |
|
836 d->actionAdded(actionEvent); |
|
837 return true; |
|
838 } else if (event->type() == QEvent::ActionRemoved) { |
|
839 QActionEvent *actionEvent = static_cast<QActionEvent *>(event); |
|
840 d->actionRemoved(actionEvent); |
|
841 return true; |
|
842 } else if (event->type() == QEvent::ActionChanged) { |
|
843 QActionEvent *actionEvent = static_cast<QActionEvent *>(event); |
|
844 d->actionChanged(actionEvent); |
|
845 return true; |
|
846 } |
|
847 } |
|
848 if (event->type() == QEvent::LayoutRequest) { |
|
849 resize(preferredSize()); |
|
850 if(d->menuItemView) |
|
851 d->menuItemView->contentWidget()->adjustSize(); |
|
852 if (d->mSubMenuItem) |
|
853 d->setSubMenuPosition(); |
|
854 } |
|
855 |
|
856 return HbPopup::event(event); |
|
857 } |
|
858 |
|
859 void HbMenu::polish(HbStyleParameters ¶ms) |
|
860 { |
|
861 Q_D(HbMenu); |
|
862 if (d->mSubMenuItem) { |
|
863 const QString RightMargin = "submenu-right-offset"; |
|
864 const QString DownMargin = "submenu-bottom-margin"; |
|
865 params.addParameter(RightMargin); |
|
866 params.addParameter(DownMargin); |
|
867 |
|
868 HbPopup::polish(params); |
|
869 |
|
870 if (!params.value(RightMargin).isNull()) { |
|
871 d->mRightMargin = params.value(RightMargin).toDouble(); |
|
872 } |
|
873 if (!params.value(DownMargin).isNull()) { |
|
874 d->mDownMargin = params.value(DownMargin).toDouble(); |
|
875 } |
|
876 d->setSubMenuPosition(); |
|
877 } else { |
|
878 HbPopup::polish(params); |
|
879 } |
|
880 } |
|
881 |
|
882 QPainterPath HbMenu::shape() const |
|
883 { |
|
884 QRectF sceneRect = mapRectToScene(boundingRect()); |
|
885 QRectF clipRect = sceneRect.intersected(geometry()); |
|
886 QPainterPath path; |
|
887 path.addRect(mapRectFromScene(clipRect)); |
|
888 return path; |
|
889 } |
|
890 |
|
891 /*! @alpha |
|
892 * |
|
893 * Opens the menu and returns immediately. |
|
894 */ |
|
895 void HbMenu::open( QObject *receiver, const char *member ) |
|
896 { |
|
897 Q_D(HbMenu); |
|
898 if ( d->receiverToDisconnectOnClose ) { // cant do on closeevent |
|
899 disconnect(this, SIGNAL(triggered(HbAction*)), |
|
900 d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose); |
|
901 d->receiverToDisconnectOnClose = 0; |
|
902 } |
|
903 d->memberToDisconnectOnClose.clear(); |
|
904 |
|
905 if ( receiver ) { |
|
906 connect(this, SIGNAL(triggered(HbAction*)), receiver, member); |
|
907 d->receiverToDisconnectOnClose = receiver; |
|
908 d->memberToDisconnectOnClose = member; |
|
909 } else { |
|
910 d->receiverToDisconnectOnClose = 0; |
|
911 } |
|
912 HbMenu::show(); |
|
913 } |
|
914 |
|
915 #include "moc_hbmenu.cpp" |