37 #include "hswidgethost.h" |
39 #include "hswidgethost.h" |
38 #include "hswallpaper.h" |
40 #include "hswallpaper.h" |
39 #include "hsselectbackgroundstate.h" |
41 #include "hsselectbackgroundstate.h" |
40 #include "hstrashbinwidget.h" |
42 #include "hstrashbinwidget.h" |
41 #include "hspageindicator.h" |
43 #include "hspageindicator.h" |
42 #include "hsapptranslator.h" |
|
43 #include "hswidgetpositioningonorientationchange.h" |
44 #include "hswidgetpositioningonorientationchange.h" |
44 #include "hsmenueventfactory.h" |
45 #include "hsmenueventfactory.h" |
45 #include "hshomescreenstatecommon.h" |
46 #include "hshomescreenstatecommon.h" |
|
47 #include "hstitleresolver.h" |
|
48 #include "hsmenuservice.h" |
46 |
49 |
47 // Helper macros for connecting state entry and exit actions. |
50 // Helper macros for connecting state entry and exit actions. |
48 #define ENTRY_ACTION(state, action) \ |
51 #define ENTRY_ACTION(state, action) \ |
49 connect(state, SIGNAL(entered()), SLOT(action())); |
52 connect(state, SIGNAL(entered()), SLOT(action())); |
50 #define EXIT_ACTION(state, action) \ |
53 #define EXIT_ACTION(state, action) \ |
60 |
63 |
61 |
64 |
62 namespace |
65 namespace |
63 { |
66 { |
64 const char gApplicationLibraryIconName[] = "qtg_mono_applications_all"; |
67 const char gApplicationLibraryIconName[] = "qtg_mono_applications_all"; |
65 const char gAddPageTextName[] = "txt_homescreen_opt_add_page"; |
68 |
66 const char gRemovePageTextName[] = "txt_homescreen_opt_remove_page"; |
69 //User adds a new page to home screen |
67 const char gToOnlineTextName[] = "txt_homescreen_opt_home_screen_to_online"; |
70 const char hsLocTextId_OptionsMenu_AddPage[] = "txt_homescreen_opt_add_page"; |
68 const char gToOfflineTextName[] = "txt_homescreen_opt_home_screen_to_offline"; |
71 |
69 const char gChangeWallpaperTextName[] = "txt_homescreen_list_change_wallpaper"; |
72 //User removes page from home screen |
70 const char gAddContentTextName[] = "txt_homescreen_list_add_content"; |
73 const char hsLocTextId_OptionsMenu_RemovePage[] = "txt_homescreen_opt_remove_page"; |
71 //const char gTitleOfflineTextName[] = "txt_homescreen_title_offline"; |
74 |
|
75 //Sends request to all widgets to enable data connections |
|
76 const char hsLocTextId_OptionsMenu_HsToOnline[] = "txt_homescreen_opt_home_screen_to_online"; |
|
77 |
|
78 //Sends request to all widgets to disable data connections |
|
79 const char hsLocTextId_OptionsMenu_HsToOffline[] = "txt_homescreen_opt_home_screen_to_offline"; |
|
80 |
|
81 //Home screen canvas menu item for opening picture gallery |
|
82 const char hsLocTextId_ContextMenu_ChangeWallpaper[] = "txt_homescreen_list_change_wallpaper"; |
|
83 |
|
84 //Home screen canvas menu item for opening Application library |
|
85 const char hsLocTextId_ContextMenu_AddContent[] = "txt_homescreen_list_add_content"; |
|
86 |
|
87 //Home screen options menu item for opening Task switcher |
|
88 const char hsLocTextId_OptionsMenu_TaskSwitcher[] = "txt_homescreen_opt_task_switcher"; |
|
89 |
|
90 //Heading text in confirmation dialog while removing page with content |
|
91 const char hsLocTextId_Title_RemovePage[] = "txt_homescreen_title_remove_page"; |
|
92 |
|
93 //Text in confirmation dialog while removing page with content |
|
94 const char hsLocTextId_Confirmation_RemovePage[] = "txt_homescreen_info_page_and_content_will_be_remov"; |
|
95 |
|
96 //Button in confirmation dialog while removing page with content |
|
97 const char hsLocTextId_ConfirmationButton_Ok[] = "txt_homescreen_button_ok"; |
|
98 |
|
99 //Button in confirmation dialog while removing page with content |
|
100 const char hsLocTextId_ConfirmationButton_Cancel[] = "txt_homescreen_button_cancel"; |
72 } |
101 } |
73 |
102 |
74 /*! |
103 /*! |
75 \class HsIdleState |
104 \class HsIdleState |
76 \ingroup group_hshomescreenstateplugin |
105 \ingroup group_hshomescreenstateplugin |
87 */ |
116 */ |
88 HsIdleState::HsIdleState(QState *parent) |
117 HsIdleState::HsIdleState(QState *parent) |
89 : QState(parent), |
118 : QState(parent), |
90 mView(0), mNavigationAction(0), mUiWidget(0), |
119 mView(0), mNavigationAction(0), mUiWidget(0), |
91 mTapAndHoldDistance(16), |
120 mTapAndHoldDistance(16), |
92 mPageChangeZoneWidth(60) |
121 mPageChangeZoneWidth(60), |
|
122 mTitleResolver(0), |
|
123 mZoneAnimation(0), |
|
124 mPageChanged(false), |
|
125 mAllowZoneAnimation(true), |
|
126 mPageChangeAnimation(0) |
93 { |
127 { |
94 setupStates(); |
128 setupStates(); |
95 mTimer.setSingleShot(true); |
129 mTimer.setSingleShot(true); |
|
130 mTitleResolver = new HsTitleResolver(this); |
96 } |
131 } |
97 |
132 |
98 /*! |
133 /*! |
99 Destroys this idle state. |
134 Destroys this idle state. |
100 */ |
135 */ |
101 HsIdleState::~HsIdleState() |
136 HsIdleState::~HsIdleState() |
102 { |
137 { |
|
138 delete mZoneAnimation; |
|
139 } |
|
140 |
|
141 /*! |
|
142 \copydoc QObject::eventFilter(QObject *watched, QEvent *event) |
|
143 */ |
|
144 bool HsIdleState::eventFilter(QObject *watched, QEvent *event) |
|
145 { |
|
146 switch (event->type()) { |
|
147 case QEvent::ApplicationActivate: |
|
148 action_idle_layoutNewWidgets(); |
|
149 break; |
|
150 default: |
|
151 break; |
|
152 } |
|
153 return QState::eventFilter(watched, event); |
103 } |
154 } |
104 |
155 |
105 /*! |
156 /*! |
106 \fn HsIdleState::event_applicationLibrary() |
157 \fn HsIdleState::event_applicationLibrary() |
107 |
158 |
199 this, SIGNAL(event_addPage()), state_addPage); |
250 this, SIGNAL(event_addPage()), state_addPage); |
200 state_waitInput->addTransition( |
251 state_waitInput->addTransition( |
201 this, SIGNAL(event_removePage()), state_removePage); |
252 this, SIGNAL(event_removePage()), state_removePage); |
202 state_waitInput->addTransition( |
253 state_waitInput->addTransition( |
203 this, SIGNAL(event_toggleConnection()), state_toggleConnection); |
254 this, SIGNAL(event_toggleConnection()), state_toggleConnection); |
|
255 state_waitInput->addTransition( |
|
256 this, SIGNAL(event_selectSceneWallpaper()), state_selectSceneWallpaper); |
204 |
257 |
205 state_widgetInteraction->addTransition( |
258 state_widgetInteraction->addTransition( |
206 this, SIGNAL(event_waitInput()), state_waitInput); |
259 this, SIGNAL(event_waitInput()), state_waitInput); |
207 state_widgetInteraction->addTransition( |
260 state_widgetInteraction->addTransition( |
208 this, SIGNAL(event_moveWidget()), state_moveWidget); |
261 this, SIGNAL(event_moveWidget()), state_moveWidget); |
222 |
275 |
223 state_sceneMenu->addTransition( |
276 state_sceneMenu->addTransition( |
224 this, SIGNAL(event_waitInput()), state_waitInput); |
277 this, SIGNAL(event_waitInput()), state_waitInput); |
225 state_sceneMenu->addTransition( |
278 state_sceneMenu->addTransition( |
226 this, SIGNAL(event_selectSceneWallpaper()), state_selectSceneWallpaper); |
279 this, SIGNAL(event_selectSceneWallpaper()), state_selectSceneWallpaper); |
|
280 state_sceneMenu->addTransition( |
|
281 this, SIGNAL(event_addPage()), state_addPage); |
227 |
282 |
228 state_selectSceneWallpaper->addTransition( |
283 state_selectSceneWallpaper->addTransition( |
229 state_selectSceneWallpaper, SIGNAL(event_waitInput()), state_waitInput); |
284 state_selectSceneWallpaper, SIGNAL(event_waitInput()), state_waitInput); |
230 |
285 |
231 state_addPage->addTransition(state_waitInput); |
286 state_addPage->addTransition(state_waitInput); |
238 |
293 |
239 ENTRY_ACTION(this, action_idle_setupView) |
294 ENTRY_ACTION(this, action_idle_setupView) |
240 ENTRY_ACTION(this, action_idle_layoutNewWidgets) |
295 ENTRY_ACTION(this, action_idle_layoutNewWidgets) |
241 ENTRY_ACTION(this, action_idle_showActivePage) |
296 ENTRY_ACTION(this, action_idle_showActivePage) |
242 ENTRY_ACTION(this, action_idle_connectOrientationChangeEventHandler) |
297 ENTRY_ACTION(this, action_idle_connectOrientationChangeEventHandler) |
|
298 ENTRY_ACTION(this, action_idle_setupTitle) |
|
299 ENTRY_ACTION(this, action_idle_installEventFilter) |
|
300 EXIT_ACTION(this, action_idle_cleanupTitle) |
243 EXIT_ACTION(this, action_idle_cleanupView) |
301 EXIT_ACTION(this, action_idle_cleanupView) |
244 EXIT_ACTION(this, action_idle_disconnectOrientationChangeEventHandler) |
302 EXIT_ACTION(this, action_idle_disconnectOrientationChangeEventHandler) |
|
303 EXIT_ACTION(this, action_idle_uninstallEventFilter) |
245 |
304 |
246 ENTRY_ACTION(state_waitInput, action_waitInput_updateOptionsMenu) |
305 ENTRY_ACTION(state_waitInput, action_waitInput_updateOptionsMenu) |
247 ENTRY_ACTION(state_waitInput, action_waitInput_connectMouseEventHandlers) |
306 ENTRY_ACTION(state_waitInput, action_waitInput_connectMouseEventHandlers) |
248 EXIT_ACTION(state_waitInput, action_waitInput_disconnectMouseEventHandlers) |
307 EXIT_ACTION(state_waitInput, action_waitInput_disconnectMouseEventHandlers) |
249 |
308 |
258 EXIT_ACTION(state_sceneInteraction, action_sceneInteraction_disconnectGestureTimers) |
317 EXIT_ACTION(state_sceneInteraction, action_sceneInteraction_disconnectGestureTimers) |
259 |
318 |
260 ENTRY_ACTION(state_moveWidget, action_moveWidget_reparentToControlLayer) |
319 ENTRY_ACTION(state_moveWidget, action_moveWidget_reparentToControlLayer) |
261 ENTRY_ACTION(state_moveWidget, action_moveWidget_startWidgetDragAnimation) |
320 ENTRY_ACTION(state_moveWidget, action_moveWidget_startWidgetDragAnimation) |
262 ENTRY_ACTION(state_moveWidget, action_moveWidget_connectMouseEventHandlers) |
321 ENTRY_ACTION(state_moveWidget, action_moveWidget_connectMouseEventHandlers) |
263 ENTRY_ACTION(state_moveWidget, action_moveWidget_connectGestureTimers) |
|
264 |
322 |
265 EXIT_ACTION(state_moveWidget, action_moveWidget_reparentToPage) |
323 EXIT_ACTION(state_moveWidget, action_moveWidget_reparentToPage) |
266 EXIT_ACTION(state_moveWidget, action_moveWidget_startWidgetDropAnimation) |
324 EXIT_ACTION(state_moveWidget, action_moveWidget_startWidgetDropAnimation) |
267 EXIT_ACTION(state_moveWidget, action_moveWidget_disconnectMouseEventHandlers) |
325 EXIT_ACTION(state_moveWidget, action_moveWidget_disconnectMouseEventHandlers) |
268 EXIT_ACTION(state_moveWidget, action_moveWidget_disconnectGestureTimers) |
|
269 |
326 |
270 ENTRY_ACTION(state_moveScene, action_moveScene_connectMouseEventHandlers) |
327 ENTRY_ACTION(state_moveScene, action_moveScene_connectMouseEventHandlers) |
271 EXIT_ACTION(state_moveScene, action_moveScene_disconnectMouseEventHandlers) |
328 EXIT_ACTION(state_moveScene, action_moveScene_disconnectMouseEventHandlers) |
272 |
329 |
273 ENTRY_ACTION(state_sceneMenu, action_sceneMenu_showMenu) |
330 ENTRY_ACTION(state_sceneMenu, action_sceneMenu_showMenu) |
297 Starts the page change animation based on the given \a targetPageIndex |
354 Starts the page change animation based on the given \a targetPageIndex |
298 and \a duration. |
355 and \a duration. |
299 */ |
356 */ |
300 void HsIdleState::startPageChangeAnimation(int targetPageIndex, int duration) |
357 void HsIdleState::startPageChangeAnimation(int targetPageIndex, int duration) |
301 { |
358 { |
302 QParallelAnimationGroup *animationGroup = new QParallelAnimationGroup; |
359 if (!mPageChangeAnimation) { |
303 |
360 mPageChangeAnimation = new QParallelAnimationGroup(this); |
304 QPropertyAnimation *animation = new QPropertyAnimation(mUiWidget->pageLayer(), "x"); |
361 |
305 animation->setEndValue(pageLayerXPos(targetPageIndex)); |
362 QPropertyAnimation *animation = new QPropertyAnimation(mUiWidget->pageLayer(), "x"); |
306 animation->setDuration(duration); |
363 animation->setEndValue(pageLayerXPos(targetPageIndex)); |
307 animationGroup->addAnimation(animation); |
364 animation->setDuration(duration); |
308 |
365 mPageChangeAnimation->addAnimation(animation); |
309 animation = new QPropertyAnimation(mUiWidget->sceneLayer(), "x"); |
366 |
310 animation->setEndValue((parallaxFactor() * pageLayerXPos(targetPageIndex)) - HSBOUNDARYEFFECT / 2); |
367 animation = new QPropertyAnimation(mUiWidget->sceneLayer(), "x"); |
311 animation->setDuration(duration); |
368 animation->setEndValue((parallaxFactor() * pageLayerXPos(targetPageIndex)) - HSBOUNDARYEFFECT / 2); |
312 animationGroup->addAnimation(animation); |
369 animation->setDuration(duration); |
|
370 mPageChangeAnimation->addAnimation(animation); |
|
371 } |
|
372 else { |
|
373 if (QAbstractAnimation::Stopped != mPageChangeAnimation->state()) { |
|
374 mPageChangeAnimation->stop(); |
|
375 } |
|
376 QAbstractAnimation *animation = mPageChangeAnimation->animationAt(0); |
313 |
377 |
314 animationGroup->start(QAbstractAnimation::DeleteWhenStopped); |
378 qobject_cast<QPropertyAnimation*>(animation)->setEndValue(pageLayerXPos(targetPageIndex)); |
315 |
379 qobject_cast<QPropertyAnimation*>(animation)->setDuration(duration); |
|
380 |
|
381 animation = mPageChangeAnimation->animationAt(1); |
|
382 qobject_cast<QPropertyAnimation*>(animation)->setEndValue((parallaxFactor() * pageLayerXPos(targetPageIndex)) - HSBOUNDARYEFFECT / 2); |
|
383 qobject_cast<QPropertyAnimation*>(animation)->setDuration(duration); |
|
384 } |
|
385 mPageChangeAnimation->start(); |
316 mUiWidget->showPageIndicator(); |
386 mUiWidget->showPageIndicator(); |
317 mUiWidget->setActivePage(targetPageIndex); |
387 mUiWidget->setActivePage(targetPageIndex); |
|
388 } |
|
389 |
|
390 /*! |
|
391 Starts the page change zone animation based on the given a\ duration |
|
392 */ |
|
393 void HsIdleState::startPageChangeZoneAnimation(int duration) |
|
394 { |
|
395 HsScene *scene = HsScene::instance(); |
|
396 |
|
397 int targetPageIndex = scene->activePageIndex(); |
|
398 |
|
399 if (isInLeftPageChangeZone() && 0 < targetPageIndex) { |
|
400 --targetPageIndex; |
|
401 } else if (isInRightPageChangeZone() && |
|
402 targetPageIndex < scene->pages().count()) { |
|
403 ++targetPageIndex; |
|
404 } else { |
|
405 mAllowZoneAnimation = true; |
|
406 return; |
|
407 } |
|
408 |
|
409 if (targetPageIndex == scene->pages().count() |
|
410 && scene->pages().last()->widgets().isEmpty()) { |
|
411 mAllowZoneAnimation = true; |
|
412 return; |
|
413 } |
|
414 |
|
415 if (!mZoneAnimation) { |
|
416 mZoneAnimation = new QPropertyAnimation(mUiWidget->sceneLayer(), "x"); |
|
417 } |
|
418 |
|
419 if (isInLeftPageChangeZone()) { |
|
420 mZoneAnimation->setEndValue(((parallaxFactor() * pageLayerXPos(scene->activePageIndex()))-HSBOUNDARYEFFECT/2)+HSBOUNDARYEFFECT/2); |
|
421 } else { |
|
422 mZoneAnimation->setEndValue(((parallaxFactor() * pageLayerXPos(scene->activePageIndex()))-HSBOUNDARYEFFECT/2)-HSBOUNDARYEFFECT/2); |
|
423 } |
|
424 mZoneAnimation->setDuration(duration); |
|
425 mZoneAnimation->setDirection(QAbstractAnimation::Forward); |
|
426 |
|
427 connect(mZoneAnimation, |
|
428 SIGNAL(finished()), |
|
429 SLOT(zoneAnimationFinished())); |
|
430 mZoneAnimation->start(); |
|
431 mPageChanged = true; |
318 } |
432 } |
319 |
433 |
320 /*! |
434 /*! |
321 Checks if the active widget is located inside the left or right |
435 Checks if the active widget is located inside the left or right |
322 page change zone. |
436 page change zone. |
404 { |
518 { |
405 if (!mView) { |
519 if (!mView) { |
406 mUiWidget = new HsIdleWidget; |
520 mUiWidget = new HsIdleWidget; |
407 mView = HsScene::mainWindow()->addView(mUiWidget); |
521 mView = HsScene::mainWindow()->addView(mUiWidget); |
408 mView->setContentFullScreen(); |
522 mView->setContentFullScreen(); |
409 mView->setTitle("Home Screen"/*hbTrId(gTitleOfflineTextName)*/); |
|
410 |
523 |
411 mNavigationAction = new HbAction(this); |
524 mNavigationAction = new HbAction(this); |
412 mNavigationAction->setIcon(HbIcon(gApplicationLibraryIconName)); |
525 mNavigationAction->setIcon(HbIcon(gApplicationLibraryIconName)); |
413 connect(mNavigationAction, SIGNAL(triggered()), SIGNAL(event_applicationLibrary())); |
526 connect(mNavigationAction, SIGNAL(triggered()), SIGNAL(event_applicationLibrary())); |
414 mView->setNavigationAction(mNavigationAction); |
527 mView->setNavigationAction(mNavigationAction); |
415 |
528 |
416 #ifndef Q_OS_SYMBIAN |
|
417 connect(HsAppTranslator::instance(), |
|
418 SIGNAL(languageChanged()), SLOT(translateUi())); |
|
419 #endif |
|
420 // TODO: Workaround to Qt/Hb layouting bugs. |
529 // TODO: Workaround to Qt/Hb layouting bugs. |
421 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); |
530 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); |
422 } |
531 } |
423 |
532 |
424 HsScene::mainWindow()->setCurrentView(mView); |
533 HsScene::mainWindow()->setCurrentView(mView); |
|
534 } |
|
535 |
|
536 /*! |
|
537 Sets the idle view's title. |
|
538 */ |
|
539 void HsIdleState::action_idle_setupTitle() |
|
540 { |
|
541 qDebug() << "HsIdleState::action_idle_setupTitle() - ENTRY"; |
|
542 onTitleChanged(mTitleResolver->title()); |
|
543 connect(mTitleResolver, SIGNAL(titleChanged(QString)), SLOT(onTitleChanged(QString))); |
|
544 qDebug() << "HsIdleState::action_idle_setupTitle() - EXIT"; |
|
545 } |
|
546 |
|
547 /*! |
|
548 Updates the idle view's title. |
|
549 */ |
|
550 void HsIdleState::onTitleChanged(QString title) |
|
551 { |
|
552 qDebug() << "HsIdleState::onTitleChanged() to title: " << title; |
|
553 mView->setTitle(title); |
|
554 } |
|
555 |
|
556 /*! |
|
557 |
|
558 */ |
|
559 void HsIdleState::onAddContentActionTriggered() |
|
560 { |
|
561 machine()->postEvent( |
|
562 HsMenuEventFactory::createOpenAppLibraryEvent(AddHsMenuMode)); |
|
563 } |
|
564 |
|
565 /*! |
|
566 Disconnects the idle view's title. |
|
567 */ |
|
568 void HsIdleState::action_idle_cleanupTitle() |
|
569 { |
|
570 mTitleResolver->disconnect(this); |
425 } |
571 } |
426 |
572 |
427 /*! |
573 /*! |
428 Lays out the active page's new widgets that were added |
574 Lays out the active page's new widgets that were added |
429 from the application library. |
575 from the application library. |
486 SIGNAL(orientationChanged(Qt::Orientation)), |
643 SIGNAL(orientationChanged(Qt::Orientation)), |
487 this, SLOT(onOrientationChanged(Qt::Orientation))); |
644 this, SLOT(onOrientationChanged(Qt::Orientation))); |
488 } |
645 } |
489 |
646 |
490 /*! |
647 /*! |
|
648 Disconnects the event filter. |
|
649 */ |
|
650 void HsIdleState::action_idle_uninstallEventFilter() |
|
651 { |
|
652 QCoreApplication::instance()->removeEventFilter(this); |
|
653 } |
|
654 |
|
655 /*! |
491 Updates the options menu content. |
656 Updates the options menu content. |
492 */ |
657 */ |
493 void HsIdleState::action_waitInput_updateOptionsMenu() |
658 void HsIdleState::action_waitInput_updateOptionsMenu() |
494 { |
659 { |
495 HsScene *scene = HsScene::instance(); |
660 HsScene *scene = HsScene::instance(); |
496 |
661 |
497 HbMenu *menu = new HbMenu(); |
662 HbMenu *menu = new HbMenu(); |
498 |
663 |
|
664 // Task switcher |
|
665 menu->addAction(hbTrId(hsLocTextId_OptionsMenu_TaskSwitcher), |
|
666 this, SLOT(openTaskSwitcher())); |
|
667 |
|
668 // Add content |
|
669 menu->addAction(hbTrId(hsLocTextId_ContextMenu_AddContent), |
|
670 this, SLOT(onAddContentActionTriggered())); |
|
671 |
499 // Add page |
672 // Add page |
500 if (scene->pages().count() < scene->maximumPageCount()) { |
673 if (scene->pages().count() < scene->maximumPageCount()) { |
501 menu->addAction(hbTrId(gAddPageTextName), |
674 menu->addAction(hbTrId(hsLocTextId_OptionsMenu_AddPage), |
502 this, SIGNAL(event_addPage())); |
675 this, SIGNAL(event_addPage())); |
503 } |
676 } |
|
677 |
|
678 // Change wallpaper |
|
679 menu->addAction(hbTrId(hsLocTextId_ContextMenu_ChangeWallpaper), |
|
680 this, SIGNAL(event_selectSceneWallpaper())); |
504 |
681 |
505 // Remove page |
682 // Remove page |
506 if (scene->activePage()->isRemovable()) { |
683 if (scene->activePage()->isRemovable()) { |
507 menu->addAction(hbTrId(gRemovePageTextName), |
684 menu->addAction(hbTrId(hsLocTextId_OptionsMenu_RemovePage), |
508 this, SIGNAL(event_removePage())); |
685 this, SIGNAL(event_removePage())); |
509 } |
686 } |
510 |
687 |
511 // Online / Offline |
688 // Online / Offline |
512 if (scene->isOnline()) { |
689 if (scene->isOnline()) { |
513 menu->addAction(hbTrId(gToOfflineTextName), |
690 menu->addAction(hbTrId(hsLocTextId_OptionsMenu_HsToOffline), |
514 this, SIGNAL(event_toggleConnection())); |
691 this, SIGNAL(event_toggleConnection())); |
515 } else { |
692 } else { |
516 menu->addAction(hbTrId(gToOnlineTextName), |
693 menu->addAction(hbTrId(hsLocTextId_OptionsMenu_HsToOnline), |
517 this, SIGNAL(event_toggleConnection())); |
694 this, SIGNAL(event_toggleConnection())); |
518 } |
695 } |
519 |
|
520 #ifndef Q_OS_SYMBIAN |
|
521 menu->addAction(hbTrId("txt_homescreen_opt_switch_language"), |
|
522 this, SLOT(switchLanguage())); |
|
523 #endif |
|
524 |
696 |
525 mView->setMenu(menu); |
697 mView->setMenu(menu); |
526 } |
698 } |
527 |
699 |
528 /*! |
700 /*! |
647 CONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveWidget_onMouseMoved) |
825 CONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveWidget_onMouseMoved) |
648 CONNECT_MOUSE_EVENT_HANDLER(mouseReleased, moveWidget_onMouseReleased) |
826 CONNECT_MOUSE_EVENT_HANDLER(mouseReleased, moveWidget_onMouseReleased) |
649 } |
827 } |
650 |
828 |
651 /*! |
829 /*! |
652 Connects the moveWidget state's gesture timers. |
|
653 */ |
|
654 void HsIdleState::action_moveWidget_connectGestureTimers() |
|
655 { |
|
656 mTimer.setInterval(800); |
|
657 connect(&mTimer, SIGNAL(timeout()), |
|
658 SLOT(moveWidget_onHoldTimeout())); |
|
659 } |
|
660 |
|
661 /*! |
|
662 Reparents the active widget to the active page. |
830 Reparents the active widget to the active page. |
663 */ |
831 */ |
664 void HsIdleState::action_moveWidget_reparentToPage() |
832 void HsIdleState::action_moveWidget_reparentToPage() |
665 { |
833 { |
666 HsWidgetHost *widget = HsScene::instance()->activeWidget(); |
834 HsWidgetHost *widget = HsScene::instance()->activeWidget(); |
690 DISCONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveWidget_onMouseMoved) |
858 DISCONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveWidget_onMouseMoved) |
691 DISCONNECT_MOUSE_EVENT_HANDLER(mouseReleased, moveWidget_onMouseReleased) |
859 DISCONNECT_MOUSE_EVENT_HANDLER(mouseReleased, moveWidget_onMouseReleased) |
692 } |
860 } |
693 |
861 |
694 /*! |
862 /*! |
695 Disconnects the moveWidget state's gesture timers. |
|
696 */ |
|
697 void HsIdleState::action_moveWidget_disconnectGestureTimers() |
|
698 { |
|
699 disconnect(&mTimer, SIGNAL(timeout()), |
|
700 this, SLOT(moveWidget_onHoldTimeout())); |
|
701 } |
|
702 |
|
703 /*! |
|
704 Connects the moveScene state's mouse event handlers. |
863 Connects the moveScene state's mouse event handlers. |
705 */ |
864 */ |
706 void HsIdleState::action_moveScene_connectMouseEventHandlers() |
865 void HsIdleState::action_moveScene_connectMouseEventHandlers() |
707 { |
866 { |
708 CONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveScene_onMouseMoved) |
867 CONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveScene_onMouseMoved) |
725 Shows the scene menu. |
884 Shows the scene menu. |
726 */ |
885 */ |
727 void HsIdleState::action_sceneMenu_showMenu() |
886 void HsIdleState::action_sceneMenu_showMenu() |
728 { |
887 { |
729 HbMenu menu; |
888 HbMenu menu; |
|
889 |
|
890 HbAction *addContentAction = |
|
891 menu.addAction(hbTrId(hsLocTextId_ContextMenu_AddContent)); |
|
892 |
|
893 HbAction *addPageAction = 0; |
|
894 HsScene *scene = HsScene::instance(); |
|
895 if (scene->pages().count() < scene->maximumPageCount()) { |
|
896 addPageAction = menu.addAction(hbTrId(hsLocTextId_OptionsMenu_AddPage)); |
|
897 } |
730 |
898 |
731 HbAction *changeWallpaperAction = |
899 HbAction *changeWallpaperAction = |
732 menu.addAction(hbTrId(gChangeWallpaperTextName)); |
900 menu.addAction(hbTrId(hsLocTextId_ContextMenu_ChangeWallpaper)); |
733 HbAction *addContentAction = |
901 |
734 menu.addAction(hbTrId(gAddContentTextName)); |
902 HbAction *action = menu.exec(mSceneMenuPos); |
735 |
903 if (action == addContentAction) { |
736 HbAction *action = menu.exec(mSceneMenuPos); |
904 onAddContentActionTriggered(); |
737 if (action == changeWallpaperAction) { |
905 } else if (addPageAction && action == addPageAction) { |
|
906 emit event_addPage(); |
|
907 } else if (action == changeWallpaperAction) { |
738 emit event_selectSceneWallpaper(); |
908 emit event_selectSceneWallpaper(); |
739 } else if (action == addContentAction) { |
|
740 machine()->postEvent( |
|
741 HsMenuEventFactory::createOpenAppLibraryEvent(AddHsMenuMode)); |
|
742 } else { |
909 } else { |
743 emit event_waitInput(); |
910 emit event_waitInput(); |
744 } |
911 } |
745 } |
912 } |
746 #ifdef COVERAGE_MEASUREMENT |
913 #ifdef COVERAGE_MEASUREMENT |
765 Removes an existing page from the scene. |
932 Removes an existing page from the scene. |
766 */ |
933 */ |
767 void HsIdleState::action_removePage_removePage() |
934 void HsIdleState::action_removePage_removePage() |
768 { |
935 { |
769 HsScene *scene = HsScene::instance(); |
936 HsScene *scene = HsScene::instance(); |
770 HsPage *page = scene->activePage(); |
937 HsPage *page = scene->activePage(); |
771 int pageIndex = scene->activePageIndex(); |
938 bool deletePage(true); |
772 |
939 |
773 mUiWidget->removePage(pageIndex); |
940 #ifndef HOMESCREEN_TEST //We don't want to test message box. |
774 scene->removePage(page); |
941 if (!page->widgets().isEmpty()) { |
775 delete page; |
942 HbMessageBox box(HbMessageBox::MessageTypeQuestion); |
776 |
943 box.setHeadingWidget(new HbLabel |
777 pageIndex = pageIndex == 0 ? 0 : pageIndex - 1; |
944 (hbTrId(hsLocTextId_Title_RemovePage))); |
778 scene->setActivePageIndex(pageIndex); |
945 box.setText(hbTrId(hsLocTextId_Confirmation_RemovePage)); |
779 |
946 box.setPrimaryAction(new HbAction(hbTrId(hsLocTextId_ConfirmationButton_Ok))); |
780 startPageChangeAnimation(pageIndex, 200); |
947 box.setSecondaryAction(new HbAction(hbTrId(hsLocTextId_ConfirmationButton_Cancel))); |
781 |
948 |
782 mUiWidget->pageIndicator()->removeItem(pageIndex); |
949 HsScene::mainWindow()->setInteractive(true); |
783 mUiWidget->setActivePage(pageIndex); |
950 HbAction *action = box.exec(); |
784 mUiWidget->showPageIndicator(); |
951 if (action != box.primaryAction()) { |
|
952 deletePage = false; |
|
953 } |
|
954 } |
|
955 #endif //HOMESCREEN_TEST |
|
956 |
|
957 if (deletePage) { |
|
958 int pageIndex = scene->activePageIndex(); |
|
959 |
|
960 mUiWidget->removePage(pageIndex); |
|
961 scene->removePage(page); |
|
962 delete page; |
|
963 |
|
964 pageIndex = pageIndex == 0 ? 0 : pageIndex - 1; |
|
965 scene->setActivePageIndex(pageIndex); |
|
966 |
|
967 startPageChangeAnimation(pageIndex, 200); |
|
968 |
|
969 mUiWidget->pageIndicator()->removeItem(pageIndex); |
|
970 mUiWidget->setActivePage(pageIndex); |
|
971 mUiWidget->showPageIndicator(); |
|
972 } |
785 } |
973 } |
786 |
974 |
787 /*! |
975 /*! |
788 Toggles the homescreen online/offline state. |
976 Toggles the homescreen online/offline state. |
789 */ |
977 */ |
931 */ |
1121 */ |
932 void HsIdleState::moveWidget_onMouseMoved( |
1122 void HsIdleState::moveWidget_onMouseMoved( |
933 QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered) |
1123 QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered) |
934 { |
1124 { |
935 Q_UNUSED(watched) |
1125 Q_UNUSED(watched) |
936 Q_UNUSED(filtered) |
1126 |
937 |
1127 HsScene *scene = HsScene::instance(); |
938 HsWidgetHost *widget = HsScene::instance()->activeWidget(); |
1128 HsWidgetHost *widget = scene->activeWidget(); |
939 Q_ASSERT(widget); |
1129 HsPage *page = scene->activePage(); |
940 |
1130 |
941 QPointF delta(event->scenePos() - event->lastScenePos()); |
1131 QPointF delta(event->scenePos() - event->lastScenePos()); |
942 QRectF region = mView->rect().adjusted(10, 55, -10, -10); |
1132 |
943 QPointF position = widget->geometry().center() + delta; |
1133 QRectF widgetRect = widget->geometry(); |
944 if (!region.contains(position)) { |
1134 widgetRect.moveTopLeft(widgetRect.topLeft() + delta); |
945 position.setX(qBound(region.left(), position.x(), region.right())); |
1135 |
946 position.setY(qBound(region.top(), position.y(), region.bottom())); |
1136 QRectF pageRect = mView->rect(); |
947 } |
1137 |
948 |
1138 qreal lowerBoundX = -widgetRect.width() / 2 + 10; |
949 widget->setPos(position - widget->rect().center()); |
1139 if (page == scene->pages().first()) { |
950 |
1140 lowerBoundX = 0; |
951 if (mTimer.isActive()) { |
1141 } |
952 if (mTapAndHoldDistance < delta.manhattanLength()) { |
1142 qreal upperBoundX = pageRect.width() - widgetRect.width() / 2 - 10; |
953 mTimer.stop(); |
1143 if (page == scene->pages().last() && scene->pages().count() == scene->maximumPageCount()) { |
|
1144 upperBoundX = pageRect.width() - widgetRect.width(); |
|
1145 } |
|
1146 |
|
1147 qreal widgetX = qBound(lowerBoundX, widgetRect.x(), upperBoundX); |
|
1148 qreal widgetY = qBound(qreal(64), widgetRect.y(), pageRect.height() - widgetRect.height()); |
|
1149 |
|
1150 /* if using ItemClipsChildrenToShape-flag in widgethost then |
|
1151 setPos does not update position here, however setGeometry does it, QT bug? |
|
1152 */ |
|
1153 widget->setGeometry(widgetX, widgetY, widgetRect.width(), widgetRect.height()); |
|
1154 |
|
1155 if (isInPageChangeZone() && mAllowZoneAnimation) { |
|
1156 if (!mZoneAnimation) { |
|
1157 /* We don't want to create another animation |
|
1158 before previous is finished */ |
|
1159 mAllowZoneAnimation = false; |
|
1160 startPageChangeZoneAnimation(800); |
954 } |
1161 } |
955 } else { |
1162 } |
956 if (isInPageChangeZone()) { |
1163 else if (mZoneAnimation && !isInPageChangeZone()) { |
957 mTimer.start(); |
1164 if (mZoneAnimation->state() == QAbstractAnimation::Running) { |
|
1165 if (mZoneAnimation->direction() == QAbstractAnimation::Forward) { |
|
1166 mPageChanged = false; |
|
1167 mZoneAnimation->setDuration(200); |
|
1168 mZoneAnimation->setDirection(QAbstractAnimation::Backward); |
|
1169 } |
|
1170 } |
|
1171 else { |
|
1172 // Out of the page change zone. Delete animation. |
|
1173 delete mZoneAnimation; |
|
1174 mZoneAnimation = NULL; |
|
1175 mAllowZoneAnimation = true; |
958 } |
1176 } |
|
1177 } |
|
1178 else if (!isInPageChangeZone()) { |
|
1179 /* Zone animation finished and deleted. |
|
1180 New animation can be launched. */ |
|
1181 mAllowZoneAnimation = true; |
959 } |
1182 } |
960 |
1183 |
961 if (mUiWidget->trashBin()->isUnderMouse()) { |
1184 if (mUiWidget->trashBin()->isUnderMouse()) { |
962 mUiWidget->trashBin()->activate(); |
1185 mUiWidget->trashBin()->activate(); |
963 } else { |
1186 } else { |
1001 widget->deleteWidgetPresentation(Qt::Vertical); |
1233 widget->deleteWidgetPresentation(Qt::Vertical); |
1002 } else { |
1234 } else { |
1003 widget->deleteWidgetPresentation(Qt::Horizontal); |
1235 widget->deleteWidgetPresentation(Qt::Horizontal); |
1004 } |
1236 } |
1005 } |
1237 } |
|
1238 |
|
1239 QRectF widgetRect = widget->geometry(); |
|
1240 QRectF pageRect = page->rect(); |
|
1241 qreal widgetX = qBound(qreal(0), widgetRect.x(), pageRect.width() - widgetRect.width()); |
|
1242 qreal widgetY = qBound(qreal(64), widgetRect.y(), pageRect.height() - widgetRect.height()); |
|
1243 widget->setPos(widgetX, widgetY); |
|
1244 |
1006 widget->setWidgetPresentation(); |
1245 widget->setWidgetPresentation(); |
1007 page->updateZValues(); |
1246 page->updateZValues(); |
1008 } |
1247 } |
1009 |
1248 mAllowZoneAnimation = true; |
1010 filtered = true; |
1249 filtered = true; |
1011 emit event_waitInput(); |
1250 emit event_waitInput(); |
1012 } |
1251 } |
1013 |
1252 |
1014 /*! |
1253 /*! |
1075 { |
1314 { |
1076 QList<HsPage *> pages = HsScene::instance()->pages(); |
1315 QList<HsPage *> pages = HsScene::instance()->pages(); |
1077 QList<HsWidgetHost *> widgets; |
1316 QList<HsWidgetHost *> widgets; |
1078 HsWidgetHost *widget = 0; |
1317 HsWidgetHost *widget = 0; |
1079 |
1318 |
1080 const int KChromeHeight = 64; // TODO: get this somewhere |
1319 QRectF pageRect = HsScene::mainWindow()->layoutRect(); |
1081 const int KWidgetAdjust = 10; // TODO: get this somewhere |
1320 |
1082 |
1321 // TODO: Temporary workaround for the Orbit bug. |
|
1322 if (orientation == Qt::Horizontal) { |
|
1323 pageRect = QRectF(0, 0, 640, 360); |
|
1324 } else { |
|
1325 pageRect = QRectF(0, 0, 360, 640); |
|
1326 } |
|
1327 // End of the workaround. |
|
1328 |
|
1329 const int chromeHeight = 64; // TODO: get this somewhere |
|
1330 |
1083 for (int i = 0; i < pages.count(); ++i) { |
1331 for (int i = 0; i < pages.count(); ++i) { |
1084 widgets = pages[i]->widgets(); |
1332 widgets = pages[i]->widgets(); |
1085 for (int j = 0; j < widgets.count(); ++j) { |
1333 for (int j = 0; j < widgets.count(); ++j) { |
1086 widget = widgets[j]; |
1334 widget = widgets[j]; |
1087 HsWidgetPresentationData presentation = widget->widgetPresentation(orientation); |
1335 HsWidgetPresentationData presentation = widget->widgetPresentation(orientation); |
1088 if (presentation.widgetId < 0) { |
1336 if (presentation.widgetId < 0) { |
1089 QList<QRectF> geometries = |
1337 QList<QRectF> geometries = |
1090 HsWidgetPositioningOnOrientationChange::instance()->convert( |
1338 HsWidgetPositioningOnOrientationChange::instance()->convert( |
1091 QRectF(0, KChromeHeight, pages[i]->rect().height(), pages[i]->rect().width()-KChromeHeight).adjusted(KWidgetAdjust, KWidgetAdjust, -KWidgetAdjust, -KWidgetAdjust), |
1339 QRectF(0, chromeHeight, |
|
1340 pageRect.height(), |
|
1341 pageRect.width() - chromeHeight), |
1092 QList<QRectF>() << widget->geometry(), |
1342 QList<QRectF>() << widget->geometry(), |
1093 QRectF(0, KChromeHeight, pages[i]->rect().width(), pages[i]->rect().height()-KChromeHeight).adjusted(KWidgetAdjust, KWidgetAdjust, -KWidgetAdjust, -KWidgetAdjust)); |
1343 QRectF(0, chromeHeight, |
|
1344 pageRect.width(), |
|
1345 pageRect.height() - chromeHeight)); |
1094 widget->setGeometry(geometries.first()); |
1346 widget->setGeometry(geometries.first()); |
1095 widget->setWidgetPresentation(); |
1347 widget->setWidgetPresentation(); |
1096 } else { |
1348 } else { |
1097 widget->setGeometry(presentation.geometry()); |
1349 widget->setPos(presentation.x, presentation.y); |
1098 widget->setZValue(presentation.zValue); |
1350 widget->setZValue(presentation.zValue); |
1099 } |
1351 } |
1100 } |
1352 } |
1101 } |
1353 } |
1102 |
1354 |
1119 { |
1371 { |
1120 mUiWidget->clearDelayedPress(); |
1372 mUiWidget->clearDelayedPress(); |
1121 emit event_sceneMenu(); |
1373 emit event_sceneMenu(); |
1122 } |
1374 } |
1123 |
1375 |
1124 /*! |
|
1125 Handles page change zone hold events for the moveWidget state. |
|
1126 */ |
|
1127 void HsIdleState::moveWidget_onHoldTimeout() |
|
1128 { |
|
1129 HsScene *scene = HsScene::instance(); |
|
1130 |
|
1131 int pageIndex = scene->activePageIndex(); |
|
1132 |
|
1133 if (isInLeftPageChangeZone() && |
|
1134 0 < pageIndex) { |
|
1135 --pageIndex; |
|
1136 } else if (isInRightPageChangeZone() && |
|
1137 pageIndex < scene->pages().count()) { |
|
1138 ++pageIndex; |
|
1139 } else { |
|
1140 return; |
|
1141 } |
|
1142 |
|
1143 if (pageIndex == scene->pages().count()) { |
|
1144 if (scene->pages().last()->widgets().isEmpty()) { |
|
1145 return; |
|
1146 } else if (scene->pages().count() < scene->maximumPageCount()) { |
|
1147 addPageToScene(pageIndex); |
|
1148 mUiWidget->showPageIndicator(); |
|
1149 mUiWidget->pageIndicator()->addItem(pageIndex); |
|
1150 } else { |
|
1151 return; |
|
1152 } |
|
1153 } |
|
1154 |
|
1155 scene->setActivePageIndex(pageIndex); |
|
1156 startPageChangeAnimation(pageIndex, 200); |
|
1157 } |
|
1158 |
|
1159 #ifndef Q_OS_SYMBIAN |
|
1160 #ifdef COVERAGE_MEASUREMENT |
1376 #ifdef COVERAGE_MEASUREMENT |
1161 #pragma CTC SKIP |
1377 #pragma CTC SKIP |
1162 #endif //COVERAGE_MEASUREMENT |
1378 #endif //COVERAGE_MEASUREMENT |
1163 /*! |
1379 |
1164 Switch the home screen language. |
1380 /*! |
1165 */ |
1381 Open task switcher. |
1166 void HsIdleState::switchLanguage() |
1382 \retval true if operation is successful. |
1167 { |
1383 */ |
1168 QString locale; |
1384 bool HsIdleState::openTaskSwitcher() |
1169 QFile file("hslocale.txt"); |
1385 { |
1170 QTextStream stream(&file); |
1386 return HsMenuService::launchTaskSwitcher(); |
1171 if (file.open(QIODevice::ReadWrite | QIODevice::Text)) { |
|
1172 QString word; |
|
1173 stream >> word; |
|
1174 if (!word.isEmpty()) { |
|
1175 locale = word; |
|
1176 } else { |
|
1177 locale = "en_US"; |
|
1178 } |
|
1179 } else { |
|
1180 locale = QLocale::system().name(); |
|
1181 } |
|
1182 |
|
1183 if (locale == "en_US") { |
|
1184 locale = "fi_FI"; |
|
1185 } else { |
|
1186 locale = "en_US"; |
|
1187 } |
|
1188 |
|
1189 file.seek(0); |
|
1190 stream << locale; |
|
1191 file.close(); |
|
1192 |
|
1193 QEvent event(QEvent::LocaleChange); |
|
1194 QApplication::sendEvent(qApp, &event); |
|
1195 } |
|
1196 |
|
1197 /*! |
|
1198 Translates the home screen ui. |
|
1199 */ |
|
1200 void HsIdleState::translateUi() |
|
1201 { |
|
1202 mView->setTitle("Home Screen"/*hbTrId(gTitleOfflineTextName)*/); |
|
1203 action_waitInput_updateOptionsMenu(); |
|
1204 } |
1387 } |
1205 #ifdef COVERAGE_MEASUREMENT |
1388 #ifdef COVERAGE_MEASUREMENT |
1206 #pragma CTC ENDSKIP |
1389 #pragma CTC ENDSKIP |
1207 #endif //COVERAGE_MEASUREMENT |
1390 #endif //COVERAGE_MEASUREMENT |
1208 #endif // Q_OS_SYMBIAN |
1391 |
1209 |
1392 /*! |
|
1393 Page change zone animation has been finished. |
|
1394 */ |
|
1395 void HsIdleState::zoneAnimationFinished() |
|
1396 { |
|
1397 HsScene *scene = HsScene::instance(); |
|
1398 int pageIndex = scene->activePageIndex(); |
|
1399 |
|
1400 if (mPageChanged) { |
|
1401 if (isInLeftPageChangeZone() && |
|
1402 0 < pageIndex) { |
|
1403 --pageIndex; |
|
1404 } |
|
1405 else if (isInRightPageChangeZone() && |
|
1406 pageIndex < scene->pages().count()) { |
|
1407 ++pageIndex; |
|
1408 } |
|
1409 else { |
|
1410 delete mZoneAnimation; |
|
1411 mZoneAnimation = NULL; |
|
1412 mAllowZoneAnimation = true; |
|
1413 return; |
|
1414 } |
|
1415 |
|
1416 if (pageIndex == scene->pages().count()) { |
|
1417 if (scene->pages().last()->widgets().isEmpty()) { |
|
1418 delete mZoneAnimation; |
|
1419 mZoneAnimation = NULL; |
|
1420 mAllowZoneAnimation = true; |
|
1421 return; |
|
1422 } |
|
1423 else if (scene->pages().count() < scene->maximumPageCount()) { |
|
1424 addPageToScene(pageIndex); |
|
1425 mUiWidget->showPageIndicator(); |
|
1426 mUiWidget->pageIndicator()->addItem(pageIndex); |
|
1427 } |
|
1428 else { |
|
1429 delete mZoneAnimation; |
|
1430 mZoneAnimation = NULL; |
|
1431 mAllowZoneAnimation = true; |
|
1432 return; |
|
1433 } |
|
1434 } |
|
1435 scene->setActivePageIndex(pageIndex); |
|
1436 startPageChangeAnimation(pageIndex, 200); |
|
1437 } |
|
1438 else { |
|
1439 scene->setActivePageIndex(pageIndex); |
|
1440 mUiWidget->setActivePage(pageIndex); |
|
1441 mAllowZoneAnimation = true; |
|
1442 } |
|
1443 |
|
1444 delete mZoneAnimation; |
|
1445 mZoneAnimation = NULL; |
|
1446 } |
1210 // Undefine the helper macros. |
1447 // Undefine the helper macros. |
1211 #undef ENTRY_ACTION |
1448 #undef ENTRY_ACTION |
1212 #undef EXIT_ACTION |
1449 #undef EXIT_ACTION |
1213 #undef CONNECT_MOUSE_EVENT_HANDLER |
1450 #undef CONNECT_MOUSE_EVENT_HANDLER |
1214 #undef DISCONNECT_MOUSE_EVENT_HANDLER |
1451 #undef DISCONNECT_MOUSE_EVENT_HANDLER |