VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.cpp

Last change on this file was 104251, checked in by vboxsync, 2 months ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in the manager UI classes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
Line 
1/* $Id: UIToolsModel.cpp 104251 2024-04-09 12:36:47Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIToolsModel class implementation.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QGraphicsScene>
30#include <QGraphicsSceneContextMenuEvent>
31#include <QGraphicsSceneMouseEvent>
32#include <QGraphicsView>
33#include <QScrollBar>
34#include <QTimer>
35
36/* GUI includes: */
37#include "QIMessageBox.h"
38#include "UIActionPoolManager.h"
39#include "UIIconPool.h"
40#include "UILoggingDefs.h"
41#include "UITools.h"
42#include "UIToolsHandlerMouse.h"
43#include "UIToolsHandlerKeyboard.h"
44#include "UIToolsModel.h"
45#include "UITranslationEventListener.h"
46#include "UIExtraDataDefs.h"
47#include "UIExtraDataManager.h"
48#include "UIMessageCenter.h"
49#include "UIModalWindowManager.h"
50#include "UIVirtualBoxManagerWidget.h"
51#include "UIVirtualBoxEventHandler.h"
52
53/* COM includes: */
54#include "CExtPack.h"
55#include "CExtPackManager.h"
56
57/* Qt includes: */
58#include <QParallelAnimationGroup>
59
60/* Type defs: */
61typedef QSet<QString> UIStringSet;
62
63
64UIToolsModel::UIToolsModel(UIToolClass enmClass, UITools *pParent)
65 : QObject(pParent)
66 , m_enmClass(enmClass)
67 , m_pTools(pParent)
68 , m_pScene(0)
69 , m_pMouseHandler(0)
70 , m_pKeyboardHandler(0)
71 , m_fItemsEnabled(true)
72{
73 prepare();
74}
75
76UIToolsModel::~UIToolsModel()
77{
78 cleanup();
79}
80
81void UIToolsModel::init()
82{
83 /* Update linked values: */
84 updateLayout();
85 updateNavigation();
86 sltItemMinimumWidthHintChanged();
87 sltItemMinimumHeightHintChanged();
88
89 /* Load settings: */
90 loadSettings();
91}
92
93UITools *UIToolsModel::tools() const
94{
95 return m_pTools;
96}
97
98UIActionPool *UIToolsModel::actionPool() const
99{
100 return tools() ? tools()->actionPool() : 0;
101}
102
103QGraphicsScene *UIToolsModel::scene() const
104{
105 return m_pScene;
106}
107
108QPaintDevice *UIToolsModel::paintDevice() const
109{
110 if (scene() && !scene()->views().isEmpty())
111 return scene()->views().first();
112 return 0;
113}
114
115QGraphicsItem *UIToolsModel::itemAt(const QPointF &position, const QTransform &deviceTransform /* = QTransform() */) const
116{
117 return scene() ? scene()->itemAt(position, deviceTransform) : 0;
118}
119
120void UIToolsModel::setToolsType(UIToolType enmType)
121{
122 if (!currentItem() || currentItem()->itemType() != enmType)
123 {
124 foreach (UIToolsItem *pItem, items())
125 if (pItem->itemType() == enmType)
126 {
127 setCurrentItem(pItem);
128 break;
129 }
130 }
131}
132
133UIToolType UIToolsModel::toolsType() const
134{
135 return currentItem() ? currentItem()->itemType() : UIToolType_Invalid;
136}
137
138void UIToolsModel::setItemsEnabled(bool fEnabled)
139{
140 if (m_fItemsEnabled != fEnabled)
141 {
142 m_fItemsEnabled = fEnabled;
143 foreach (UIToolsItem *pItem, items())
144 pItem->setEnabled(m_fItemsEnabled);
145 }
146}
147
148bool UIToolsModel::isItemsEnabled() const
149{
150 return m_fItemsEnabled;
151}
152
153void UIToolsModel::setRestrictedToolTypes(const QList<UIToolType> &types)
154{
155 if (m_restrictedToolTypes != types)
156 {
157 m_restrictedToolTypes = types;
158 foreach (UIToolsItem *pItem, items())
159 pItem->setVisible(!m_restrictedToolTypes.contains(pItem->itemType()));
160 updateLayout();
161 updateNavigation();
162 sltItemMinimumWidthHintChanged();
163 sltItemMinimumHeightHintChanged();
164 }
165}
166
167QList<UIToolType> UIToolsModel::restrictedToolTypes() const
168{
169 return m_restrictedToolTypes;
170}
171
172void UIToolsModel::close()
173{
174 emit sigClose();
175}
176
177void UIToolsModel::setCurrentItem(UIToolsItem *pItem)
178{
179 /* Is there something changed? */
180 if (m_pCurrentItem == pItem)
181 return;
182
183 /* Remember old current-item: */
184 UIToolsItem *pOldCurrentItem = m_pCurrentItem;
185
186 /* If there is item: */
187 if (pItem)
188 {
189 /* Set this item as current: */
190 m_pCurrentItem = pItem;
191
192 /* Load last tool types: */
193 UIToolType enmTypeGlobal, enmTypeMachine;
194 loadLastToolTypes(enmTypeGlobal, enmTypeMachine);
195
196 /* Depending on tool class: */
197 switch (m_enmClass)
198 {
199 case UIToolClass_Global: enmTypeGlobal = m_pCurrentItem->itemType(); break;
200 case UIToolClass_Machine: enmTypeMachine = m_pCurrentItem->itemType(); break;
201 default: break;
202 }
203
204 /* Save selected items data: */
205 const QList<UIToolType> currentTypes = QList<UIToolType>() << enmTypeGlobal << enmTypeMachine;
206 LogRel2(("GUI: UIToolsModel: Saving tool items as: Global=%d, Machine=%d\n",
207 (int)enmTypeGlobal, (int)enmTypeMachine));
208 gEDataManager->setToolsPaneLastItemsChosen(currentTypes);
209 }
210 /* Otherwise reset current item: */
211 else
212 m_pCurrentItem = 0;
213
214 /* Update old item (if any): */
215 if (pOldCurrentItem)
216 pOldCurrentItem->update();
217 /* Update new item (if any): */
218 if (m_pCurrentItem)
219 m_pCurrentItem->update();
220
221 /* Notify about selection change: */
222 emit sigSelectionChanged(toolsType());
223
224 /* Move focus to current-item: */
225 setFocusItem(currentItem());
226
227 /* Adjust corrresponding actions finally: */
228 const UIToolType enmType = currentItem() ? currentItem()->itemType() : UIToolType_Welcome;
229 QMap<UIToolType, UIAction*> actions;
230 actions[UIToolType_Welcome] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_WelcomeScreen);
231 actions[UIToolType_Extensions] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_ExtensionPackManager);
232 actions[UIToolType_Media] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_VirtualMediaManager);
233 actions[UIToolType_Network] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_NetworkManager);
234 actions[UIToolType_Cloud] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_CloudProfileManager);
235 actions[UIToolType_VMActivityOverview] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_VMActivityOverview);
236 if (actions.contains(enmType))
237 actions.value(enmType)->setChecked(true);
238}
239
240UIToolsItem *UIToolsModel::currentItem() const
241{
242 return m_pCurrentItem;
243}
244
245void UIToolsModel::setFocusItem(UIToolsItem *pItem)
246{
247 /* Always make sure real focus unset: */
248 scene()->setFocusItem(0);
249
250 /* Is there something changed? */
251 if (m_pFocusItem == pItem)
252 return;
253
254 /* Remember old focus-item: */
255 UIToolsItem *pOldFocusItem = m_pFocusItem;
256
257 /* If there is item: */
258 if (pItem)
259 {
260 /* Set this item to focus if navigation list contains it: */
261 if (navigationList().contains(pItem))
262 m_pFocusItem = pItem;
263 /* Otherwise it's error: */
264 else
265 AssertMsgFailed(("Passed item is not in navigation list!"));
266 }
267 /* Otherwise reset focus item: */
268 else
269 m_pFocusItem = 0;
270
271 /* Disconnect old focus-item (if any): */
272 if (pOldFocusItem)
273 disconnect(pOldFocusItem, &UIToolsItem::destroyed, this, &UIToolsModel::sltFocusItemDestroyed);
274 /* Connect new focus-item (if any): */
275 if (m_pFocusItem)
276 connect(m_pFocusItem.data(), &UIToolsItem::destroyed, this, &UIToolsModel::sltFocusItemDestroyed);
277
278 /* Notify about focus change: */
279 emit sigFocusChanged();
280}
281
282UIToolsItem *UIToolsModel::focusItem() const
283{
284 return m_pFocusItem;
285}
286
287const QList<UIToolsItem*> &UIToolsModel::navigationList() const
288{
289 return m_navigationList;
290}
291
292void UIToolsModel::removeFromNavigationList(UIToolsItem *pItem)
293{
294 AssertMsg(pItem, ("Passed item is invalid!"));
295 m_navigationList.removeAll(pItem);
296}
297
298void UIToolsModel::updateNavigation()
299{
300 /* Clear list initially: */
301 m_navigationList.clear();
302
303 /* Enumerate the children: */
304 foreach (UIToolsItem *pItem, items())
305 if (pItem->isVisible())
306 m_navigationList << pItem;
307}
308
309QList<UIToolsItem*> UIToolsModel::items() const
310{
311 return m_items;
312}
313
314UIToolsItem *UIToolsModel::item(UIToolType enmType) const
315{
316 foreach (UIToolsItem *pItem, items())
317 if (pItem->itemType() == enmType)
318 return pItem;
319 return 0;
320}
321
322void UIToolsModel::updateLayout()
323{
324 /* Prepare variables: */
325 const int iMargin = data(ToolsModelData_Margin).toInt();
326 const int iSpacing = data(ToolsModelData_Spacing).toInt();
327 const QSize viewportSize = scene()->views()[0]->viewport()->size();
328 const int iViewportWidth = viewportSize.width();
329 int iVerticalIndent = iMargin;
330
331 /* Layout the children: */
332 foreach (UIToolsItem *pItem, items())
333 {
334 /* Make sure item visible: */
335 if (!pItem->isVisible())
336 continue;
337
338 /* Set item position: */
339 pItem->setPos(iMargin, iVerticalIndent);
340 /* Set root-item size: */
341 pItem->resize(iViewportWidth, pItem->minimumHeightHint());
342 /* Make sure item is shown: */
343 pItem->show();
344 /* Advance vertical indent: */
345 iVerticalIndent += (pItem->minimumHeightHint() + iSpacing);
346 }
347}
348
349void UIToolsModel::sltItemMinimumWidthHintChanged()
350{
351 /* Prepare variables: */
352 const int iMargin = data(ToolsModelData_Margin).toInt();
353
354 /* Calculate maximum horizontal width: */
355 int iMinimumWidthHint = 0;
356 iMinimumWidthHint += 2 * iMargin;
357 foreach (UIToolsItem *pItem, items())
358 iMinimumWidthHint = qMax(iMinimumWidthHint, pItem->minimumWidthHint());
359
360 /* Notify listeners: */
361 emit sigItemMinimumWidthHintChanged(iMinimumWidthHint);
362}
363
364void UIToolsModel::sltItemMinimumHeightHintChanged()
365{
366 /* Prepare variables: */
367 const int iMargin = data(ToolsModelData_Margin).toInt();
368 const int iSpacing = data(ToolsModelData_Spacing).toInt();
369
370 /* Calculate summary vertical height: */
371 int iMinimumHeightHint = 0;
372 iMinimumHeightHint += 2 * iMargin;
373 foreach (UIToolsItem *pItem, items())
374 if (pItem->isVisible())
375 iMinimumHeightHint += (pItem->minimumHeightHint() + iSpacing);
376 iMinimumHeightHint -= iSpacing;
377
378 /* Notify listeners: */
379 emit sigItemMinimumHeightHintChanged(iMinimumHeightHint);
380}
381
382bool UIToolsModel::eventFilter(QObject *pWatched, QEvent *pEvent)
383{
384 /* Process only scene events: */
385 if (pWatched != scene())
386 return QObject::eventFilter(pWatched, pEvent);
387
388 /* Process only item focused by model: */
389 if (scene()->focusItem())
390 return QObject::eventFilter(pWatched, pEvent);
391
392 /* Do not handle disabled items: */
393 if (!currentItem()->isEnabled())
394 return QObject::eventFilter(pWatched, pEvent);
395
396 /* Checking event-type: */
397 switch (pEvent->type())
398 {
399 /* Keyboard handler: */
400 case QEvent::KeyPress:
401 return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Press);
402 case QEvent::KeyRelease:
403 return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Release);
404 /* Mouse handler: */
405 case QEvent::GraphicsSceneMousePress:
406 return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Press);
407 case QEvent::GraphicsSceneMouseRelease:
408 return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Release);
409 /* Shut up MSC: */
410 default: break;
411 }
412
413 /* Call to base-class: */
414 return QObject::eventFilter(pWatched, pEvent);
415}
416
417void UIToolsModel::sltRetranslateUI()
418{
419 foreach (UIToolsItem *pItem, m_items)
420 {
421 switch (pItem->itemType())
422 {
423 case UIToolType_Welcome: pItem->reconfigure(tr("Welcome")); break;
424 case UIToolType_Extensions: pItem->reconfigure(tr("Extensions")); break;
425 case UIToolType_Media: pItem->reconfigure(tr("Media")); break;
426 case UIToolType_Network: pItem->reconfigure(tr("Network")); break;
427 case UIToolType_Cloud: pItem->reconfigure(tr("Cloud")); break;
428 case UIToolType_VMActivityOverview: pItem->reconfigure(tr("Activities")); break;
429 case UIToolType_Details: pItem->reconfigure(tr("Details")); break;
430 case UIToolType_Snapshots: pItem->reconfigure(tr("Snapshots")); break;
431 case UIToolType_Logs: pItem->reconfigure(tr("Logs")); break;
432 case UIToolType_VMActivity: pItem->reconfigure(tr("Activity")); break;
433 case UIToolType_FileManager: pItem->reconfigure(tr("File Manager")); break;
434 default: break;
435 }
436 }
437}
438
439void UIToolsModel::sltFocusItemDestroyed()
440{
441 AssertMsgFailed(("Focus item destroyed!"));
442}
443
444void UIToolsModel::prepare()
445{
446 /* Prepare scene: */
447 prepareScene();
448 /* Prepare items: */
449 prepareItems();
450 /* Prepare handlers: */
451 prepareHandlers();
452 /* Apply language settings: */
453 sltRetranslateUI();
454 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
455 this, &UIToolsModel::sltRetranslateUI);
456}
457
458void UIToolsModel::prepareScene()
459{
460 m_pScene = new QGraphicsScene(this);
461 if (m_pScene)
462 m_pScene->installEventFilter(this);
463}
464
465void UIToolsModel::prepareItems()
466{
467 /* Depending on tool class: */
468 switch (m_enmClass)
469 {
470 case UIToolClass_Global:
471 {
472 /* Welcome: */
473 m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Welcome, QString(),
474 UIIconPool::iconSet(":/welcome_screen_24px.png",
475 ":/welcome_screen_24px.png"));
476
477 /* Extensions: */
478 m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Extensions, QString(),
479 UIIconPool::iconSet(":/extension_pack_manager_24px.png",
480 ":/extension_pack_manager_disabled_24px.png"));
481
482 /* Media: */
483 m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Media, QString(),
484 UIIconPool::iconSet(":/media_manager_24px.png",
485 ":/media_manager_disabled_24px.png"));
486
487 /* Network: */
488 m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Network, QString(),
489 UIIconPool::iconSet(":/host_iface_manager_24px.png",
490 ":/host_iface_manager_disabled_24px.png"));
491
492 /* Cloud: */
493 m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Cloud, QString(),
494 UIIconPool::iconSet(":/cloud_profile_manager_24px.png",
495 ":/cloud_profile_manager_disabled_24px.png"));
496
497 /* Activities: */
498 m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_VMActivityOverview, QString(),
499 UIIconPool::iconSet(":/resources_monitor_24px.png",
500 ":/resources_monitor_disabled_24px.png"));
501
502 break;
503 }
504 case UIToolClass_Machine:
505 {
506 /* Details: */
507 m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_Details, QString(),
508 UIIconPool::iconSet(":/machine_details_manager_24px.png",
509 ":/machine_details_manager_disabled_24px.png"));
510
511 /* Snapshots: */
512 m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_Snapshots, QString(),
513 UIIconPool::iconSet(":/snapshot_manager_24px.png",
514 ":/snapshot_manager_disabled_24px.png"));
515
516 /* Logs: */
517 m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_Logs, QString(),
518 UIIconPool::iconSet(":/vm_show_logs_24px.png",
519 ":/vm_show_logs_disabled_24px.png"));
520
521 /* Activity: */
522 m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_VMActivity, QString(),
523 UIIconPool::iconSet(":/performance_monitor_24px.png",
524 ":/performance_monitor_disabled_24px.png"));
525
526 /* File Manager: */
527 m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_FileManager, QString(),
528 UIIconPool::iconSet(":/file_manager_24px.png",
529 ":/file_manager_disabled_24px.png"));
530
531 break;
532 }
533 default:
534 break;
535 }
536}
537
538void UIToolsModel::prepareHandlers()
539{
540 m_pMouseHandler = new UIToolsHandlerMouse(this);
541 m_pKeyboardHandler = new UIToolsHandlerKeyboard(this);
542}
543
544void UIToolsModel::loadSettings()
545{
546 /* Load last tool types: */
547 UIToolType enmTypeGlobal, enmTypeMachine;
548 loadLastToolTypes(enmTypeGlobal, enmTypeMachine);
549
550 /* Depending on tool class: */
551 UIToolsItem *pCurrentItem = 0;
552 switch (m_enmClass)
553 {
554 case UIToolClass_Global:
555 {
556 foreach (UIToolsItem *pItem, items())
557 if (pItem->itemType() == enmTypeGlobal)
558 pCurrentItem = pItem;
559 if (!pCurrentItem)
560 pCurrentItem = item(UIToolType_Welcome);
561 break;
562 }
563 case UIToolClass_Machine:
564 {
565 foreach (UIToolsItem *pItem, items())
566 if (pItem->itemType() == enmTypeMachine)
567 pCurrentItem = pItem;
568 if (!pCurrentItem)
569 pCurrentItem = item(UIToolType_Details);
570 break;
571 }
572 default:
573 break;
574 }
575 setCurrentItem(pCurrentItem);
576}
577
578/* static */
579void UIToolsModel::loadLastToolTypes(UIToolType &enmTypeGlobal, UIToolType &enmTypeMachine)
580{
581 /* Load selected items data: */
582 const QList<UIToolType> data = gEDataManager->toolsPaneLastItemsChosen();
583 enmTypeGlobal = data.value(0);
584 if (!UIToolStuff::isTypeOfClass(enmTypeGlobal, UIToolClass_Global))
585 enmTypeGlobal = UIToolType_Welcome;
586 enmTypeMachine = data.value(1);
587 if (!UIToolStuff::isTypeOfClass(enmTypeMachine, UIToolClass_Machine))
588 enmTypeMachine = UIToolType_Details;
589 LogRel2(("GUI: UIToolsModel: Restoring tool items as: Global=%d, Machine=%d\n",
590 (int)enmTypeGlobal, (int)enmTypeMachine));
591}
592
593void UIToolsModel::cleanupHandlers()
594{
595 delete m_pKeyboardHandler;
596 m_pKeyboardHandler = 0;
597 delete m_pMouseHandler;
598 m_pMouseHandler = 0;
599}
600
601void UIToolsModel::cleanupItems()
602{
603 foreach (UIToolsItem *pItem, m_items)
604 delete pItem;
605 m_items.clear();
606}
607
608void UIToolsModel::cleanupScene()
609{
610 delete m_pScene;
611 m_pScene = 0;
612}
613
614void UIToolsModel::cleanup()
615{
616 /* Cleanup handlers: */
617 cleanupHandlers();
618 /* Cleanup items: */
619 cleanupItems();
620 /* Cleanup scene: */
621 cleanupScene();
622}
623
624QVariant UIToolsModel::data(int iKey) const
625{
626 /* Provide other members with required data: */
627 switch (iKey)
628 {
629 /* Layout hints: */
630 case ToolsModelData_Margin: return 0;
631 case ToolsModelData_Spacing: return 1;
632
633 /* Default: */
634 default: break;
635 }
636 return QVariant();
637}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use