VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.cpp@ 84910

Last change on this file since 84910 was 84910, checked in by vboxsync, 4 years ago

FE/Qt: bugref:9774: VirtualBox Manager: Add Visual State option to Details pane, allow to edit it via embedded popup editor.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/VBox-3.0/src/VBox/Frontends/VirtualBox/src/selector/graphics/chooser/UIGChooserItemMachine.cpp58652,​70973
    /branches/VBox-3.2/src/VBox/Frontends/VirtualBox/src/selector/graphics/chooser/UIGChooserItemMachine.cpp66309,​66318
    /branches/VBox-4.0/src/VBox/Frontends/VirtualBox/src/selector/graphics/chooser/UIGChooserItemMachine.cpp70873
    /branches/VBox-4.1/src/VBox/Frontends/VirtualBox/src/selector/graphics/chooser/UIGChooserItemMachine.cpp74233
    /branches/VBox-4.2/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGDetailsElement.cpp91503-91504,​91506-91508,​91510,​91514-91515,​91521
    /branches/VBox-4.3/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGDetailsElement.cpp91223
    /branches/VBox-4.3/trunk/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGDetailsElement.cpp91223
    /branches/dsen/gui/src/VBox/Frontends/VirtualBox/src/selector/graphics/chooser/UIGChooserItemMachine.cpp79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
    /branches/dsen/gui2/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGDetailsElement.cpp79562-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
    /branches/dsen/gui3/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGDetailsElement.cpp79645-79692
    /trunk/src/VBox/Frontends/VirtualBox/src/selector/graphics/chooser/UIGChooserItemMachine.cpp79225,​79271
File size: 54.2 KB
Line 
1/* $Id: UIDetailsElement.cpp 84910 2020-06-22 16:10:09Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIDetailsElement class implementation.
4 */
5
6/*
7 * Copyright (C) 2012-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QActionGroup>
20#include <QGraphicsSceneMouseEvent>
21#include <QGraphicsView>
22#include <QPropertyAnimation>
23#include <QSignalTransition>
24#include <QStateMachine>
25#include <QStyleOptionGraphicsItem>
26
27/* GUI includes: */
28#include "QIDialogContainer.h"
29#include "UIActionPool.h"
30#include "UIAudioControllerEditor.h"
31#include "UIAudioHostDriverEditor.h"
32#include "UIBaseMemoryEditor.h"
33#include "UIBootOrderEditor.h"
34#include "UICloudMachineSettingsDialogPage.h"
35#include "UICloudNetworkingStuff.h"
36#include "UICommon.h"
37#include "UIConverter.h"
38#include "UIDetailsElement.h"
39#include "UIDetailsSet.h"
40#include "UIDetailsModel.h"
41#include "UIExtraDataManager.h"
42#include "UIGraphicsControllerEditor.h"
43#include "UIGraphicsRotatorButton.h"
44#include "UIGraphicsTextPane.h"
45#include "UIIconPool.h"
46#include "UIMachineAttributeSetter.h"
47#include "UINameAndSystemEditor.h"
48#include "UINetworkAttachmentEditor.h"
49#include "UITaskCloudGetSettingsForm.h"
50#include "UIThreadPool.h"
51#include "UIVideoMemoryEditor.h"
52#include "UIVirtualBoxManager.h"
53#include "UIVisualStateEditor.h"
54
55
56/** Known anchor roles. */
57enum AnchorRole
58{
59 AnchorRole_Invalid,
60 AnchorRole_MachineName,
61 AnchorRole_MachineLocation,
62 AnchorRole_OSType,
63 AnchorRole_BaseMemory,
64 AnchorRole_BootOrder,
65 AnchorRole_VideoMemory,
66 AnchorRole_GraphicsControllerType,
67 AnchorRole_Storage,
68 AnchorRole_AudioHostDriverType,
69 AnchorRole_AudioControllerType,
70 AnchorRole_NetworkAttachmentType,
71 AnchorRole_USBControllerType,
72 AnchorRole_VisualStateType,
73#ifndef VBOX_WS_MAC
74 AnchorRole_MenuBar,
75#endif
76 AnchorRole_StatusBar,
77#ifndef VBOX_WS_MAC
78 AnchorRole_MiniToolbar,
79#endif
80 AnchorRole_Cloud,
81};
82
83
84UIDetailsElement::UIDetailsElement(UIDetailsSet *pParent, DetailsElementType enmType, bool fOpened)
85 : UIDetailsItem(pParent)
86 , m_pSet(pParent)
87 , m_enmType(enmType)
88#ifdef VBOX_WS_MAC
89 , m_iDefaultToneStart(145)
90 , m_iDefaultToneFinal(155)
91 , m_iHoverToneStart(115)
92 , m_iHoverToneFinal(125)
93#else
94 , m_iDefaultToneStart(160)
95 , m_iDefaultToneFinal(190)
96 , m_iHoverToneStart(160)
97 , m_iHoverToneFinal(190)
98#endif
99 , m_fHovered(false)
100 , m_fNameHovered(false)
101 , m_pHoveringMachine(0)
102 , m_pHoveringAnimationForward(0)
103 , m_pHoveringAnimationBackward(0)
104 , m_iAnimationDuration(300)
105 , m_iDefaultValue(0)
106 , m_iHoveredValue(255)
107 , m_iAnimatedValue(m_iDefaultValue)
108 , m_pButton(0)
109 , m_fClosed(!fOpened)
110 , m_fAnimationRunning(false)
111 , m_iAdditionalHeight(0)
112 , m_pTextPane(0)
113 , m_iMinimumHeaderWidth(0)
114 , m_iMinimumHeaderHeight(0)
115{
116 /* Prepare element: */
117 prepareElement();
118 /* Prepare button: */
119 prepareButton();
120 /* Prepare text-pane: */
121 prepareTextPane();
122
123 /* Setup size-policy: */
124 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
125
126 /* Add item to the parent: */
127 AssertMsg(parentItem(), ("No parent set for details element!"));
128 parentItem()->addItem(this);
129}
130
131UIDetailsElement::~UIDetailsElement()
132{
133 /* Remove item from the parent: */
134 AssertMsg(parentItem(), ("No parent set for details element!"));
135 parentItem()->removeItem(this);
136}
137
138void UIDetailsElement::setText(const UITextTable &text)
139{
140 /* Pass text to text-pane: */
141 m_pTextPane->setText(text);
142}
143
144UITextTable &UIDetailsElement::text() const
145{
146 /* Retrieve text from text-pane: */
147 return m_pTextPane->text();
148}
149
150void UIDetailsElement::close(bool fAnimated /* = true */)
151{
152 m_pButton->setToggled(false, fAnimated);
153}
154
155void UIDetailsElement::open(bool fAnimated /* = true */)
156{
157 m_pButton->setToggled(true, fAnimated);
158}
159
160void UIDetailsElement::markAnimationFinished()
161{
162 /* Mark animation as non-running: */
163 m_fAnimationRunning = false;
164
165 /* Recursively update size-hint: */
166 updateGeometry();
167 /* Repaint: */
168 update();
169}
170
171void UIDetailsElement::updateAppearance()
172{
173 /* Reset name hover state: */
174 m_fNameHovered = false;
175 updateNameHoverLink();
176
177 /* Update anchor role restrictions: */
178 const ConfigurationAccessLevel enmCal = m_pSet->configurationAccessLevel();
179 m_pTextPane->setAnchorRoleRestricted("#machine_name", enmCal != ConfigurationAccessLevel_Full
180 && enmCal != ConfigurationAccessLevel_Partial_Saved);
181 m_pTextPane->setAnchorRoleRestricted("#machine_location", enmCal != ConfigurationAccessLevel_Full);
182 m_pTextPane->setAnchorRoleRestricted("#os_type", enmCal != ConfigurationAccessLevel_Full);
183 m_pTextPane->setAnchorRoleRestricted("#base_memory", enmCal != ConfigurationAccessLevel_Full);
184 m_pTextPane->setAnchorRoleRestricted("#boot_order", enmCal != ConfigurationAccessLevel_Full);
185 m_pTextPane->setAnchorRoleRestricted("#video_memory", enmCal != ConfigurationAccessLevel_Full);
186 m_pTextPane->setAnchorRoleRestricted("#graphics_controller_type", enmCal != ConfigurationAccessLevel_Full);
187 m_pTextPane->setAnchorRoleRestricted("#mount", enmCal == ConfigurationAccessLevel_Null);
188 m_pTextPane->setAnchorRoleRestricted("#attach", enmCal != ConfigurationAccessLevel_Full);
189 m_pTextPane->setAnchorRoleRestricted("#audio_host_driver_type", enmCal != ConfigurationAccessLevel_Full
190 && enmCal != ConfigurationAccessLevel_Partial_Saved);
191 m_pTextPane->setAnchorRoleRestricted("#audio_controller_type", enmCal != ConfigurationAccessLevel_Full);
192 m_pTextPane->setAnchorRoleRestricted("#network_attachment_type", enmCal == ConfigurationAccessLevel_Null);
193 m_pTextPane->setAnchorRoleRestricted("#usb_controller_type", enmCal != ConfigurationAccessLevel_Full);
194 m_pTextPane->setAnchorRoleRestricted("#visual_state", enmCal == ConfigurationAccessLevel_Null);
195#ifndef VBOX_WS_MAC
196 m_pTextPane->setAnchorRoleRestricted("#menu_bar", enmCal == ConfigurationAccessLevel_Null);
197#endif
198 m_pTextPane->setAnchorRoleRestricted("#status_bar", enmCal == ConfigurationAccessLevel_Null);
199#ifndef VBOX_WS_MAC
200 m_pTextPane->setAnchorRoleRestricted("#mini_toolbar", enmCal == ConfigurationAccessLevel_Null);
201#endif
202}
203
204int UIDetailsElement::minimumWidthHint() const
205{
206 /* Prepare variables: */
207 int iMargin = data(ElementData_Margin).toInt();
208 int iMinimumWidthHint = 0;
209
210 /* Maximum width: */
211 iMinimumWidthHint = qMax(m_iMinimumHeaderWidth, (int)m_pTextPane->minimumSizeHint().width());
212
213 /* And 4 margins: 2 left and 2 right: */
214 iMinimumWidthHint += 4 * iMargin;
215
216 /* Return result: */
217 return iMinimumWidthHint;
218}
219
220int UIDetailsElement::minimumHeightHint() const
221{
222 return minimumHeightHintForElement(m_fClosed);
223}
224
225void UIDetailsElement::showEvent(QShowEvent *pEvent)
226{
227 /* Call to base-class: */
228 UIDetailsItem::showEvent(pEvent);
229
230 /* Update icon: */
231 updateIcon();
232}
233
234void UIDetailsElement::resizeEvent(QGraphicsSceneResizeEvent*)
235{
236 /* Update layout: */
237 updateLayout();
238}
239
240void UIDetailsElement::hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent)
241{
242 /* Update hover state: */
243 if (!m_fHovered)
244 {
245 m_fHovered = true;
246 emit sigHoverEnter();
247 }
248
249 /* Update name-hover state: */
250 handleHoverEvent(pEvent);
251}
252
253void UIDetailsElement::hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent)
254{
255 /* Update hover state: */
256 if (m_fHovered)
257 {
258 m_fHovered = false;
259 emit sigHoverLeave();
260 }
261
262 /* Update name-hover state: */
263 handleHoverEvent(pEvent);
264}
265
266void UIDetailsElement::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
267{
268 /* Only for hovered header: */
269 if (!m_fNameHovered)
270 return;
271
272 /* Process link click: */
273 pEvent->accept();
274 QString strCategory;
275 if (m_enmType >= DetailsElementType_General &&
276 m_enmType < DetailsElementType_Description)
277 strCategory = QString("#%1").arg(gpConverter->toInternalString(m_enmType));
278 else if (m_enmType == DetailsElementType_Description)
279 strCategory = QString("#%1%%mTeDescription").arg(gpConverter->toInternalString(m_enmType));
280 emit sigLinkClicked(strCategory, QString(), machine().GetId());
281}
282
283void UIDetailsElement::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *pEvent)
284{
285 /* Only for left-button: */
286 if (pEvent->button() != Qt::LeftButton)
287 return;
288
289 /* Process left-button double-click: */
290 emit sigToggleElement(m_enmType, isClosed());
291}
292
293void UIDetailsElement::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *)
294{
295 /* Update button visibility: */
296 updateButtonVisibility();
297
298 /* Paint background: */
299 paintBackground(pPainter, pOptions);
300 /* Paint frame: */
301 paintFrame(pPainter, pOptions);
302 /* Paint element info: */
303 paintElementInfo(pPainter, pOptions);
304}
305
306QString UIDetailsElement::description() const
307{
308 return tr("%1 details", "like 'General details' or 'Storage details'").arg(m_strName);
309}
310
311const CMachine &UIDetailsElement::machine()
312{
313 return m_pSet->machine();
314}
315
316const CCloudMachine &UIDetailsElement::cloudMachine()
317{
318 return m_pSet->cloudMachine();
319}
320
321bool UIDetailsElement::isLocal() const
322{
323 return m_pSet->isLocal();
324}
325
326void UIDetailsElement::setName(const QString &strName)
327{
328 /* Cache name: */
329 m_strName = strName;
330 QFontMetrics fm(m_nameFont, model()->paintDevice());
331 m_nameSize = QSize(fm.width(m_strName), fm.height());
332
333 /* Update linked values: */
334 updateMinimumHeaderWidth();
335 updateMinimumHeaderHeight();
336}
337
338void UIDetailsElement::setAdditionalHeight(int iAdditionalHeight)
339{
340 /* Cache new value: */
341 m_iAdditionalHeight = iAdditionalHeight;
342 /* Update layout: */
343 updateLayout();
344 /* Repaint: */
345 update();
346}
347
348QVariant UIDetailsElement::data(int iKey) const
349{
350 /* Provide other members with required data: */
351 switch (iKey)
352 {
353 /* Hints: */
354 case ElementData_Margin: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
355 case ElementData_Spacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
356 /* Default: */
357 default: break;
358 }
359 return QVariant();
360}
361
362void UIDetailsElement::addItem(UIDetailsItem*)
363{
364 AssertMsgFailed(("Details element do NOT support children!"));
365}
366
367void UIDetailsElement::removeItem(UIDetailsItem*)
368{
369 AssertMsgFailed(("Details element do NOT support children!"));
370}
371
372QList<UIDetailsItem*> UIDetailsElement::items(UIDetailsItemType) const
373{
374 AssertMsgFailed(("Details element do NOT support children!"));
375 return QList<UIDetailsItem*>();
376}
377
378bool UIDetailsElement::hasItems(UIDetailsItemType) const
379{
380 AssertMsgFailed(("Details element do NOT support children!"));
381 return false;
382}
383
384void UIDetailsElement::clearItems(UIDetailsItemType)
385{
386 AssertMsgFailed(("Details element do NOT support children!"));
387}
388
389void UIDetailsElement::updateLayout()
390{
391 /* Prepare variables: */
392 QSize size = geometry().size().toSize();
393 int iMargin = data(ElementData_Margin).toInt();
394
395 /* Layout button: */
396 int iButtonWidth = m_buttonSize.width();
397 int iButtonHeight = m_buttonSize.height();
398 int iButtonX = size.width() - 2 * iMargin - iButtonWidth;
399 int iButtonY = iButtonHeight == m_iMinimumHeaderHeight ? iMargin :
400 iMargin + (m_iMinimumHeaderHeight - iButtonHeight) / 2;
401 m_pButton->setPos(iButtonX, iButtonY);
402
403 /* If closed: */
404 if (isClosed())
405 {
406 /* Hide text-pane if still visible: */
407 if (m_pTextPane->isVisible())
408 m_pTextPane->hide();
409 }
410 /* If opened: */
411 else
412 {
413 /* Layout text-pane: */
414 int iTextPaneX = 2 * iMargin;
415 int iTextPaneY = iMargin + m_iMinimumHeaderHeight + 2 * iMargin;
416 m_pTextPane->setPos(iTextPaneX, iTextPaneY);
417 m_pTextPane->resize(size.width() - 4 * iMargin,
418 size.height() - 4 * iMargin - m_iMinimumHeaderHeight);
419 /* Show text-pane if still invisible and animation finished: */
420 if (!m_pTextPane->isVisible() && !isAnimationRunning())
421 m_pTextPane->show();
422 }
423}
424
425int UIDetailsElement::minimumHeightHintForElement(bool fClosed) const
426{
427 /* Prepare variables: */
428 int iMargin = data(ElementData_Margin).toInt();
429 int iMinimumHeightHint = 0;
430
431 /* Two margins: */
432 iMinimumHeightHint += 2 * iMargin;
433
434 /* Header height: */
435 iMinimumHeightHint += m_iMinimumHeaderHeight;
436
437 /* Element is opened? */
438 if (!fClosed)
439 {
440 /* Add text height: */
441 if (!m_pTextPane->isEmpty())
442 iMinimumHeightHint += 2 * iMargin + (int)m_pTextPane->minimumSizeHint().height();
443 }
444
445 /* Additional height during animation: */
446 if (m_fAnimationRunning)
447 iMinimumHeightHint += m_iAdditionalHeight;
448
449 /* Return value: */
450 return iMinimumHeightHint;
451}
452
453void UIDetailsElement::sltHandleWindowRemapped()
454{
455 /* Update icon: */
456 updateIcon();
457}
458
459void UIDetailsElement::sltToggleButtonClicked()
460{
461 emit sigToggleElement(m_enmType, isClosed());
462}
463
464void UIDetailsElement::sltElementToggleStart()
465{
466 /* Mark animation running: */
467 m_fAnimationRunning = true;
468
469 /* Setup animation: */
470 updateAnimationParameters();
471
472 /* Invert toggle-state: */
473 m_fClosed = !m_fClosed;
474}
475
476void UIDetailsElement::sltElementToggleFinish(bool fToggled)
477{
478 /* Update toggle-state: */
479 m_fClosed = !fToggled;
480
481 /* Notify about finishing: */
482 emit sigToggleElementFinished();
483}
484
485void UIDetailsElement::sltHandleAnchorClicked(const QString &strAnchor)
486{
487 /* Compose a map of known anchor roles: */
488 QMap<QString, AnchorRole> roles;
489 roles["#machine_name"] = AnchorRole_MachineName;
490 roles["#machine_location"] = AnchorRole_MachineLocation;
491 roles["#os_type"] = AnchorRole_OSType;
492 roles["#base_memory"] = AnchorRole_BaseMemory;
493 roles["#boot_order"] = AnchorRole_BootOrder;
494 roles["#video_memory"] = AnchorRole_VideoMemory;
495 roles["#graphics_controller_type"] = AnchorRole_GraphicsControllerType;
496 roles["#mount"] = AnchorRole_Storage;
497 roles["#attach"] = AnchorRole_Storage;
498 roles["#audio_host_driver_type"] = AnchorRole_AudioHostDriverType;
499 roles["#audio_controller_type"] = AnchorRole_AudioControllerType;
500 roles["#network_attachment_type"] = AnchorRole_NetworkAttachmentType;
501 roles["#usb_controller_type"] = AnchorRole_USBControllerType;
502 roles["#visual_state"] = AnchorRole_VisualStateType;
503#ifndef VBOX_WS_MAC
504 roles["#menu_bar"] = AnchorRole_MenuBar;
505#endif
506 roles["#status_bar"] = AnchorRole_StatusBar;
507#ifndef VBOX_WS_MAC
508 roles["#mini_toolbar"] = AnchorRole_MiniToolbar;
509#endif
510 roles["#cloud"] = AnchorRole_Cloud;
511
512 /* Current anchor role: */
513 const QString strRole = strAnchor.section(',', 0, 0);
514 const QString strData = strAnchor.section(',', 1);
515
516 /* Handle known anchor roles: */
517 const AnchorRole enmRole = roles.value(strRole, AnchorRole_Invalid);
518 switch (enmRole)
519 {
520 case AnchorRole_MachineName:
521 case AnchorRole_MachineLocation:
522 case AnchorRole_OSType:
523 {
524 /* Prepare popup: */
525 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
526 if (pPopup)
527 {
528 /* Prepare editor: */
529 UINameAndSystemEditor *pEditor =
530 new UINameAndSystemEditor(pPopup,
531 enmRole == AnchorRole_MachineName /* choose name? */,
532 enmRole == AnchorRole_MachineLocation /* choose path? */,
533 enmRole == AnchorRole_OSType /* choose type? */);
534 if (pEditor)
535 {
536 switch (enmRole)
537 {
538 case AnchorRole_MachineName: pEditor->setName(strData.section(',', 0, 0)); break;
539 case AnchorRole_MachineLocation: pEditor->setPath(strData.section(',', 0, 0)); break;
540 case AnchorRole_OSType: pEditor->setTypeId(strData.section(',', 0, 0)); break;
541 default: break;
542 }
543
544 /* Add to popup: */
545 pPopup->setWidget(pEditor);
546 }
547
548 /* Adjust popup geometry: */
549 pPopup->move(QCursor::pos());
550 pPopup->adjustSize();
551
552 // WORKAROUND:
553 // On Windows, Tool dialogs aren't activated by default by some reason.
554 // So we have created sltActivateWindow wrapping actual activateWindow
555 // to fix that annoying issue.
556 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
557 /* Execute popup, change machine name if confirmed: */
558 if (pPopup->exec() == QDialog::Accepted)
559 {
560 switch (enmRole)
561 {
562 case AnchorRole_MachineName: setMachineAttribute(machine(), MachineAttribute_Name, QVariant::fromValue(pEditor->name())); break;
563 case AnchorRole_MachineLocation: setMachineAttribute(machine(), MachineAttribute_Location, QVariant::fromValue(pEditor->path())); break;
564 case AnchorRole_OSType: setMachineAttribute(machine(), MachineAttribute_OSType, QVariant::fromValue(pEditor->typeId())); break;
565 default: break;
566 }
567 }
568
569 /* Delete popup: */
570 delete pPopup;
571 }
572 break;
573 }
574 case AnchorRole_BaseMemory:
575 {
576 /* Prepare popup: */
577 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
578 if (pPopup)
579 {
580 /* Prepare editor: */
581 UIBaseMemoryEditor *pEditor = new UIBaseMemoryEditor(pPopup, true /* with label */);
582 if (pEditor)
583 {
584 pEditor->setValue(strData.section(',', 0, 0).toInt());
585 connect(pEditor, &UIBaseMemoryEditor::sigValidChanged,
586 pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
587 pPopup->setWidget(pEditor);
588 }
589
590 /* Adjust popup geometry: */
591 pPopup->move(QCursor::pos());
592 pPopup->adjustSize();
593
594 // WORKAROUND:
595 // On Windows, Tool dialogs aren't activated by default by some reason.
596 // So we have created sltActivateWindow wrapping actual activateWindow
597 // to fix that annoying issue.
598 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
599 /* Execute popup, change machine name if confirmed: */
600 if (pPopup->exec() == QDialog::Accepted)
601 setMachineAttribute(machine(), MachineAttribute_BaseMemory, QVariant::fromValue(pEditor->value()));
602
603 /* Delete popup: */
604 delete pPopup;
605 }
606 break;
607 }
608 case AnchorRole_BootOrder:
609 {
610 /* Prepare popup: */
611 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
612 if (pPopup)
613 {
614 /* Prepare editor: */
615 UIBootOrderEditor *pEditor = new UIBootOrderEditor(pPopup, true /* with label */);
616 if (pEditor)
617 {
618 pEditor->setValue(bootItemsFromSerializedString(strData.section(',', 0, 0)));
619 pPopup->setWidget(pEditor);
620 }
621
622 /* Adjust popup geometry: */
623 pPopup->move(QCursor::pos());
624 pPopup->adjustSize();
625
626 // WORKAROUND:
627 // On Windows, Tool dialogs aren't activated by default by some reason.
628 // So we have created sltActivateWindow wrapping actual activateWindow
629 // to fix that annoying issue.
630 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
631 /* Execute popup, change machine name if confirmed: */
632 if (pPopup->exec() == QDialog::Accepted)
633 setMachineAttribute(machine(), MachineAttribute_BootOrder, QVariant::fromValue(pEditor->value()));
634
635 /* Delete popup: */
636 delete pPopup;
637 }
638 break;
639 }
640 case AnchorRole_VideoMemory:
641 {
642 /* Prepare popup: */
643 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
644 if (pPopup)
645 {
646 /* Prepare editor: */
647 UIVideoMemoryEditor *pEditor = new UIVideoMemoryEditor(pPopup, true /* with label */);
648 if (pEditor)
649 {
650 pEditor->setValue(strData.section(',', 0, 0).toInt());
651 connect(pEditor, &UIVideoMemoryEditor::sigValidChanged,
652 pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
653 pPopup->setWidget(pEditor);
654 }
655
656 /* Adjust popup geometry: */
657 pPopup->move(QCursor::pos());
658 pPopup->adjustSize();
659
660 // WORKAROUND:
661 // On Windows, Tool dialogs aren't activated by default by some reason.
662 // So we have created sltActivateWindow wrapping actual activateWindow
663 // to fix that annoying issue.
664 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
665 /* Execute popup, change machine name if confirmed: */
666 if (pPopup->exec() == QDialog::Accepted)
667 setMachineAttribute(machine(), MachineAttribute_VideoMemory, QVariant::fromValue(pEditor->value()));
668
669 /* Delete popup: */
670 delete pPopup;
671 }
672 break;
673 }
674 case AnchorRole_GraphicsControllerType:
675 {
676 /* Prepare popup: */
677 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
678 if (pPopup)
679 {
680 /* Prepare editor: */
681 UIGraphicsControllerEditor *pEditor = new UIGraphicsControllerEditor(pPopup, true /* with label */);
682 if (pEditor)
683 {
684 pEditor->setValue(static_cast<KGraphicsControllerType>(strData.section(',', 0, 0).toInt()));
685 pPopup->setWidget(pEditor);
686 }
687
688 /* Adjust popup geometry: */
689 pPopup->move(QCursor::pos());
690 pPopup->adjustSize();
691
692 // WORKAROUND:
693 // On Windows, Tool dialogs aren't activated by default by some reason.
694 // So we have created sltActivateWindow wrapping actual activateWindow
695 // to fix that annoying issue.
696 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
697 /* Execute popup, change machine name if confirmed: */
698 if (pPopup->exec() == QDialog::Accepted)
699 setMachineAttribute(machine(), MachineAttribute_GraphicsControllerType, QVariant::fromValue(pEditor->value()));
700
701 /* Delete popup: */
702 delete pPopup;
703 }
704 break;
705 }
706 case AnchorRole_Storage:
707 {
708 /* Prepare storage-menu: */
709 UIMenu menu;
710 menu.setShowToolTip(true);
711
712 /* Storage-controller name: */
713 QString strControllerName = strData.section(',', 0, 0);
714 /* Storage-slot: */
715 StorageSlot storageSlot = gpConverter->fromString<StorageSlot>(strData.section(',', 1));
716
717 /* Fill storage-menu: */
718 uiCommon().prepareStorageMenu(menu, this, SLOT(sltMountStorageMedium()),
719 machine(), strControllerName, storageSlot);
720
721 /* Exec menu: */
722 menu.exec(QCursor::pos());
723 break;
724 }
725 case AnchorRole_AudioHostDriverType:
726 {
727 /* Prepare popup: */
728 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
729 if (pPopup)
730 {
731 /* Prepare editor: */
732 UIAudioHostDriverEditor *pEditor = new UIAudioHostDriverEditor(pPopup, true /* with label */);
733 if (pEditor)
734 {
735 pEditor->setValue(static_cast<KAudioDriverType>(strData.section(',', 0, 0).toInt()));
736 pPopup->setWidget(pEditor);
737 }
738
739 /* Adjust popup geometry: */
740 pPopup->move(QCursor::pos());
741 pPopup->adjustSize();
742
743 // WORKAROUND:
744 // On Windows, Tool dialogs aren't activated by default by some reason.
745 // So we have created sltActivateWindow wrapping actual activateWindow
746 // to fix that annoying issue.
747 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
748 /* Execute popup, change machine name if confirmed: */
749 if (pPopup->exec() == QDialog::Accepted)
750 setMachineAttribute(machine(), MachineAttribute_AudioHostDriverType, QVariant::fromValue(pEditor->value()));
751
752 /* Delete popup: */
753 delete pPopup;
754 }
755 break;
756 }
757 case AnchorRole_AudioControllerType:
758 {
759 /* Prepare popup: */
760 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
761 if (pPopup)
762 {
763 /* Prepare editor: */
764 UIAudioControllerEditor *pEditor = new UIAudioControllerEditor(pPopup, true /* with label */);
765 if (pEditor)
766 {
767 pEditor->setValue(static_cast<KAudioControllerType>(strData.section(',', 0, 0).toInt()));
768 pPopup->setWidget(pEditor);
769 }
770
771 /* Adjust popup geometry: */
772 pPopup->move(QCursor::pos());
773 pPopup->adjustSize();
774
775 // WORKAROUND:
776 // On Windows, Tool dialogs aren't activated by default by some reason.
777 // So we have created sltActivateWindow wrapping actual activateWindow
778 // to fix that annoying issue.
779 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
780 /* Execute popup, change machine name if confirmed: */
781 if (pPopup->exec() == QDialog::Accepted)
782 setMachineAttribute(machine(), MachineAttribute_AudioControllerType, QVariant::fromValue(pEditor->value()));
783
784 /* Delete popup: */
785 delete pPopup;
786 }
787 break;
788 }
789 case AnchorRole_NetworkAttachmentType:
790 {
791 /* Prepare popup: */
792 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
793 if (pPopup)
794 {
795 /* Prepare editor: */
796 UINetworkAttachmentEditor *pEditor = new UINetworkAttachmentEditor(pPopup, true /* with label */);
797 if (pEditor)
798 {
799 pEditor->setValueNames(KNetworkAttachmentType_Bridged, UINetworkAttachmentEditor::bridgedAdapters());
800 pEditor->setValueNames(KNetworkAttachmentType_Internal, UINetworkAttachmentEditor::internalNetworks());
801 pEditor->setValueNames(KNetworkAttachmentType_HostOnly, UINetworkAttachmentEditor::hostInterfaces());
802 pEditor->setValueNames(KNetworkAttachmentType_Generic, UINetworkAttachmentEditor::genericDrivers());
803 pEditor->setValueNames(KNetworkAttachmentType_NATNetwork, UINetworkAttachmentEditor::natNetworks());
804 pEditor->setValueType(static_cast<KNetworkAttachmentType>(strData.section(',', 0, 0).section(';', 1, 1).toInt()));
805 pEditor->setValueName(pEditor->valueType(), strData.section(',', 0, 0).section(';', 2, 2));
806 connect(pEditor, &UINetworkAttachmentEditor::sigValidChanged,
807 pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
808 pPopup->setWidget(pEditor);
809 }
810
811 /* Adjust popup geometry: */
812 pPopup->move(QCursor::pos());
813 pPopup->adjustSize();
814
815 // WORKAROUND:
816 // On Windows, Tool dialogs aren't activated by default by some reason.
817 // So we have created sltActivateWindow wrapping actual activateWindow
818 // to fix that annoying issue.
819 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
820 /* Execute popup, change machine name if confirmed: */
821 if (pPopup->exec() == QDialog::Accepted)
822 {
823 UINetworkAdapterDescriptor nad(strData.section(',', 0, 0).section(';', 0, 0).toInt(),
824 pEditor->valueType(), pEditor->valueName(pEditor->valueType()));
825 setMachineAttribute(machine(), MachineAttribute_NetworkAttachmentType, QVariant::fromValue(nad));
826 }
827
828 /* Delete popup: */
829 delete pPopup;
830 }
831 break;
832 }
833 case AnchorRole_USBControllerType:
834 {
835 /* Parse controller type list: */
836 UIUSBControllerTypeSet controllerSet;
837 const QStringList controllerInternals = strData.section(',', 0, 0).split(';');
838 foreach (const QString &strControllerType, controllerInternals)
839 {
840 /* Parse each internal controller description: */
841 bool fParsed = false;
842 KUSBControllerType enmType = static_cast<KUSBControllerType>(strControllerType.toInt(&fParsed));
843 if (!fParsed)
844 enmType = KUSBControllerType_Null;
845 controllerSet << enmType;
846 }
847
848 /* Prepare existing controller sets: */
849 QMap<int, UIUSBControllerTypeSet> controllerSets;
850 controllerSets[0] = UIUSBControllerTypeSet();
851 controllerSets[1] = UIUSBControllerTypeSet() << KUSBControllerType_OHCI;
852 controllerSets[2] = UIUSBControllerTypeSet() << KUSBControllerType_OHCI << KUSBControllerType_EHCI;
853 controllerSets[3] = UIUSBControllerTypeSet() << KUSBControllerType_XHCI;
854
855 /* Fill menu with actions: */
856 UIMenu menu;
857 QActionGroup group(&menu);
858 QMap<int, QAction*> actions;
859 actions[0] = menu.addAction(QApplication::translate("UIDetails", "Disabled", "details (usb)"));
860 group.addAction(actions.value(0));
861 actions.value(0)->setCheckable(true);
862 actions[1] = menu.addAction(QApplication::translate("UIDetails", "USB 1.1 (OHCI) Controller", "details (usb)"));
863 group.addAction(actions.value(1));
864 actions.value(1)->setCheckable(true);
865 actions[2] = menu.addAction(QApplication::translate("UIDetails", "USB 2.0 (OHCI + EHCI) Controller", "details (usb)"));
866 group.addAction(actions.value(2));
867 actions.value(2)->setCheckable(true);
868 actions[3] = menu.addAction(QApplication::translate("UIDetails", "USB 3.0 (xHCI) Controller", "details (usb)"));
869 group.addAction(actions.value(3));
870 actions.value(3)->setCheckable(true);
871
872 /* Mark current one: */
873 for (int i = 0; i <= 3; ++i)
874 actions.value(i)->setChecked(controllerSets.key(controllerSet) == i);
875
876 /* Execute menu, look for result: */
877 QAction *pTriggeredAction = menu.exec(QCursor::pos());
878 if (pTriggeredAction)
879 {
880 const int iTriggeredIndex = actions.key(pTriggeredAction);
881 if (controllerSets.key(controllerSet) != iTriggeredIndex)
882 setMachineAttribute(machine(), MachineAttribute_USBControllerType, QVariant::fromValue(controllerSets.value(iTriggeredIndex)));
883 }
884 break;
885 }
886 case AnchorRole_VisualStateType:
887 {
888 /* Prepare popup: */
889 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
890 if (pPopup)
891 {
892 /* Prepare editor: */
893 UIVisualStateEditor *pEditor = new UIVisualStateEditor(pPopup, true /* with label */);
894 if (pEditor)
895 {
896 pEditor->setMachineId(machine().GetId());
897 pEditor->setValue(static_cast<UIVisualStateType>(strData.section(',', 0, 0).toInt()));
898 pPopup->setWidget(pEditor);
899 }
900
901 /* Adjust popup geometry: */
902 pPopup->move(QCursor::pos());
903 pPopup->adjustSize();
904
905 // WORKAROUND:
906 // On Windows, Tool dialogs aren't activated by default by some reason.
907 // So we have created sltActivateWindow wrapping actual activateWindow
908 // to fix that annoying issue.
909 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
910 /* Execute popup, change machine name if confirmed: */
911 if (pPopup->exec() == QDialog::Accepted)
912 gEDataManager->setRequestedVisualState(pEditor->value(), machine().GetId());
913
914 /* Delete popup: */
915 delete pPopup;
916 }
917 break;
918 }
919#ifndef VBOX_WS_MAC
920 case AnchorRole_MenuBar:
921#endif
922 case AnchorRole_StatusBar:
923 {
924 /* Parse whether we have it enabled, true if unable to parse: */
925 bool fParsed = false;
926 bool fEnabled = strData.section(',', 0, 0).toInt(&fParsed);
927 if (!fParsed)
928 fEnabled = true;
929
930 /* Fill menu with actions, use menu-bar NLS for both cases for simplicity: */
931 UIMenu menu;
932 QActionGroup group(&menu);
933 QAction *pActionDisable = menu.addAction(QApplication::translate("UIDetails", "Disabled", "details (user interface/menu-bar)"));
934 group.addAction(pActionDisable);
935 pActionDisable->setCheckable(true);
936 pActionDisable->setChecked(!fEnabled);
937 QAction *pActionEnable = menu.addAction(QApplication::translate("UIDetails", "Enabled", "details (user interface/menu-bar)"));
938 group.addAction(pActionEnable);
939 pActionEnable->setCheckable(true);
940 pActionEnable->setChecked(fEnabled);
941
942 /* Execute menu, look for result: */
943 QAction *pTriggeredAction = menu.exec(QCursor::pos());
944 if ( pTriggeredAction
945 && ( (fEnabled && pTriggeredAction == pActionDisable)
946 || (!fEnabled && pTriggeredAction == pActionEnable)))
947 {
948 switch (enmRole)
949 {
950#ifndef VBOX_WS_MAC
951 case AnchorRole_MenuBar: gEDataManager->setMenuBarEnabled(!fEnabled, machine().GetId()); break;
952#endif
953 case AnchorRole_StatusBar: gEDataManager->setStatusBarEnabled(!fEnabled, machine().GetId()); break;
954 default: break;
955 }
956 }
957 break;
958 }
959#ifndef VBOX_WS_MAC
960 case AnchorRole_MiniToolbar:
961 {
962 /* Parse whether we have it enabled: */
963 bool fParsed = false;
964 MiniToolbarAlignment enmAlignment = static_cast<MiniToolbarAlignment>(strData.section(',', 0, 0).toInt(&fParsed));
965 if (!fParsed)
966 enmAlignment = MiniToolbarAlignment_Disabled;
967
968 /* Fill menu with actions: */
969 UIMenu menu;
970 QActionGroup group(&menu);
971 QAction *pActionDisabled = menu.addAction(QApplication::translate("UIDetails", "Disabled", "details (user interface/mini-toolbar)"));
972 group.addAction(pActionDisabled);
973 pActionDisabled->setCheckable(true);
974 pActionDisabled->setChecked(enmAlignment == MiniToolbarAlignment_Disabled);
975 QAction *pActionTop = menu.addAction(QApplication::translate("UIDetails", "Top", "details (user interface/mini-toolbar position)"));
976 group.addAction(pActionTop);
977 pActionTop->setCheckable(true);
978 pActionTop->setChecked(enmAlignment == MiniToolbarAlignment_Top);
979 QAction *pActionBottom = menu.addAction(QApplication::translate("UIDetails", "Bottom", "details (user interface/mini-toolbar position)"));
980 group.addAction(pActionBottom);
981 pActionBottom->setCheckable(true);
982 pActionBottom->setChecked(enmAlignment == MiniToolbarAlignment_Bottom);
983
984 /* Execute menu, look for result: */
985 QAction *pTriggeredAction = menu.exec(QCursor::pos());
986 if (pTriggeredAction)
987 {
988 const QUuid uMachineId = machine().GetId();
989 if (pTriggeredAction == pActionDisabled)
990 gEDataManager->setMiniToolbarEnabled(false, uMachineId);
991 else if (pTriggeredAction == pActionTop)
992 {
993 gEDataManager->setMiniToolbarEnabled(true, uMachineId);
994 gEDataManager->setMiniToolbarAlignment(Qt::AlignTop, uMachineId);
995 }
996 else if (pTriggeredAction == pActionBottom)
997 {
998 gEDataManager->setMiniToolbarEnabled(true, uMachineId);
999 gEDataManager->setMiniToolbarAlignment(Qt::AlignBottom, uMachineId);
1000 }
1001 }
1002 break;
1003 }
1004#endif
1005 case AnchorRole_Cloud:
1006 {
1007 /* Prepare popup: */
1008 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
1009 if (pPopup)
1010 {
1011 /* Acquire cloud machine: */
1012 CCloudMachine comCloudMachine = cloudMachine();
1013
1014 /* Prepare editor: */
1015 UISafePointerCloudMachineSettingsDialogPage pEditor = new UICloudMachineSettingsDialogPage(pPopup,
1016 false /* full-scale? */);
1017 if (pEditor)
1018 {
1019 /* Configure editor: */
1020 connect(pEditor.data(), &UICloudMachineSettingsDialogPage::sigValidChanged,
1021 pPopup.data(), &QIDialogContainer::setProgressBarHidden);
1022 connect(pEditor.data(), &UICloudMachineSettingsDialogPage::sigValidChanged,
1023 pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
1024 pEditor->setFilter(strData);
1025 /* Create get settings form task: */
1026 UITaskCloudGetSettingsForm *pTask = new UITaskCloudGetSettingsForm(comCloudMachine);
1027 /* Create get settings form receiver: */
1028 UIReceiverCloudGetSettingsForm *pReceiver = new UIReceiverCloudGetSettingsForm(pEditor);
1029 if (pReceiver)
1030 {
1031 connect(pReceiver, &UIReceiverCloudGetSettingsForm::sigTaskComplete,
1032 pEditor.data(), &UICloudMachineSettingsDialogPage::setForm);
1033 connect(pReceiver, &UIReceiverCloudGetSettingsForm::sigTaskFailed,
1034 pPopup.data(), &QIDialogContainer::close);
1035 }
1036 /* Start task: */
1037 if (pTask && pReceiver)
1038 uiCommon().threadPoolCloud()->enqueueTask(pTask);
1039 /* Embed editor: */
1040 pPopup->setWidget(pEditor);
1041 }
1042
1043 /* Adjust popup geometry: */
1044 pPopup->move(QCursor::pos());
1045 pPopup->resize(pPopup->minimumSizeHint());
1046
1047 // WORKAROUND:
1048 // On Windows, Tool dialogs aren't activated by default by some reason.
1049 // So we have created sltActivateWindow wrapping actual activateWindow
1050 // to fix that annoying issue.
1051 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
1052 /* Execute popup, change machine name if confirmed: */
1053 if (pPopup->exec() == QDialog::Accepted)
1054 {
1055 /* Makes sure page data committed: */
1056 if (pEditor)
1057 pEditor->makeSureDataCommitted();
1058
1059 /* Apply form: */
1060 CForm comForm = pEditor->form();
1061 applyCloudMachineSettingsForm(comCloudMachine, comForm);
1062 }
1063
1064 /* Delete popup: */
1065 delete pPopup;
1066 }
1067 break;
1068 }
1069 default:
1070 break;
1071 }
1072}
1073
1074void UIDetailsElement::sltMountStorageMedium()
1075{
1076 /* Sender action: */
1077 QAction *pAction = qobject_cast<QAction*>(sender());
1078 AssertMsgReturnVoid(pAction, ("This slot should only be called by menu action!\n"));
1079
1080 /* Current mount-target: */
1081 const UIMediumTarget target = pAction->data().value<UIMediumTarget>();
1082
1083 /* Update current machine mount-target: */
1084 uiCommon().updateMachineStorage(machine(), target);
1085}
1086
1087void UIDetailsElement::prepareElement()
1088{
1089 /* Initialization: */
1090 m_nameFont = font();
1091 m_nameFont.setWeight(QFont::Bold);
1092 m_textFont = font();
1093
1094 /* Update icon: */
1095 updateIcon();
1096
1097 /* Create hovering animation machine: */
1098 m_pHoveringMachine = new QStateMachine(this);
1099 if (m_pHoveringMachine)
1100 {
1101 /* Create 'default' state: */
1102 QState *pStateDefault = new QState(m_pHoveringMachine);
1103 /* Create 'hovered' state: */
1104 QState *pStateHovered = new QState(m_pHoveringMachine);
1105
1106 /* Configure 'default' state: */
1107 if (pStateDefault)
1108 {
1109 /* When we entering default state => we assigning animatedValue to m_iDefaultValue: */
1110 pStateDefault->assignProperty(this, "animatedValue", m_iDefaultValue);
1111
1112 /* Add state transition: */
1113 QSignalTransition *pDefaultToHovered = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateHovered);
1114 if (pDefaultToHovered)
1115 {
1116 /* Create forward animation: */
1117 m_pHoveringAnimationForward = new QPropertyAnimation(this, "animatedValue", this);
1118 if (m_pHoveringAnimationForward)
1119 {
1120 m_pHoveringAnimationForward->setDuration(m_iAnimationDuration);
1121 m_pHoveringAnimationForward->setStartValue(m_iDefaultValue);
1122 m_pHoveringAnimationForward->setEndValue(m_iHoveredValue);
1123
1124 /* Add to transition: */
1125 pDefaultToHovered->addAnimation(m_pHoveringAnimationForward);
1126 }
1127 }
1128 }
1129
1130 /* Configure 'hovered' state: */
1131 if (pStateHovered)
1132 {
1133 /* When we entering hovered state => we assigning animatedValue to m_iHoveredValue: */
1134 pStateHovered->assignProperty(this, "animatedValue", m_iHoveredValue);
1135
1136 /* Add state transition: */
1137 QSignalTransition *pHoveredToDefault = pStateHovered->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
1138 if (pHoveredToDefault)
1139 {
1140 /* Create backward animation: */
1141 m_pHoveringAnimationBackward = new QPropertyAnimation(this, "animatedValue", this);
1142 if (m_pHoveringAnimationBackward)
1143 {
1144 m_pHoveringAnimationBackward->setDuration(m_iAnimationDuration);
1145 m_pHoveringAnimationBackward->setStartValue(m_iHoveredValue);
1146 m_pHoveringAnimationBackward->setEndValue(m_iDefaultValue);
1147
1148 /* Add to transition: */
1149 pHoveredToDefault->addAnimation(m_pHoveringAnimationBackward);
1150 }
1151 }
1152 }
1153
1154 /* Initial state is 'default': */
1155 m_pHoveringMachine->setInitialState(pStateDefault);
1156 /* Start state-machine: */
1157 m_pHoveringMachine->start();
1158 }
1159
1160 /* Configure connections: */
1161 connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
1162 this, &UIDetailsElement::sltHandleWindowRemapped);
1163 connect(this, &UIDetailsElement::sigToggleElement,
1164 model(), &UIDetailsModel::sltToggleElements);
1165 connect(this, &UIDetailsElement::sigLinkClicked,
1166 model(), &UIDetailsModel::sigLinkClicked);
1167}
1168
1169void UIDetailsElement::prepareButton()
1170{
1171 /* Setup toggle-button: */
1172 m_pButton = new UIGraphicsRotatorButton(this, "additionalHeight", !m_fClosed, true /* reflected */);
1173 m_pButton->setAutoHandleButtonClick(false);
1174 connect(m_pButton, &UIGraphicsRotatorButton::sigButtonClicked, this, &UIDetailsElement::sltToggleButtonClicked);
1175 connect(m_pButton, &UIGraphicsRotatorButton::sigRotationStart, this, &UIDetailsElement::sltElementToggleStart);
1176 connect(m_pButton, &UIGraphicsRotatorButton::sigRotationFinish, this, &UIDetailsElement::sltElementToggleFinish);
1177 m_buttonSize = m_pButton->minimumSizeHint().toSize();
1178}
1179
1180void UIDetailsElement::prepareTextPane()
1181{
1182 /* Create text-pane: */
1183 m_pTextPane = new UIGraphicsTextPane(this, model()->paintDevice());
1184 connect(m_pTextPane, &UIGraphicsTextPane::sigGeometryChanged, this, &UIDetailsElement::sltUpdateGeometry);
1185 connect(m_pTextPane, &UIGraphicsTextPane::sigAnchorClicked, this, &UIDetailsElement::sltHandleAnchorClicked);
1186}
1187
1188void UIDetailsElement::updateIcon()
1189{
1190 /* Prepare whole icon first of all: */
1191 const QIcon icon = gpConverter->toIcon(elementType());
1192
1193 /* Cache icon: */
1194 if (icon.isNull())
1195 {
1196 /* No icon provided: */
1197 m_pixmapSize = QSize();
1198 m_pixmap = QPixmap();
1199 }
1200 else
1201 {
1202 /* Determine default icon size: */
1203 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
1204 m_pixmapSize = QSize(iIconMetric, iIconMetric);
1205 /* Acquire the icon of corresponding size (taking top-level widget DPI into account): */
1206 m_pixmap = icon.pixmap(gpManager->windowHandle(), m_pixmapSize);
1207 }
1208
1209 /* Update linked values: */
1210 updateMinimumHeaderWidth();
1211 updateMinimumHeaderHeight();
1212}
1213
1214void UIDetailsElement::handleHoverEvent(QGraphicsSceneHoverEvent *pEvent)
1215{
1216 /* Not for 'preview' element type: */
1217 if (m_enmType == DetailsElementType_Preview)
1218 return;
1219
1220 /* Prepare variables: */
1221 int iMargin = data(ElementData_Margin).toInt();
1222 int iSpacing = data(ElementData_Spacing).toInt();
1223 int iNameHeight = m_nameSize.height();
1224 int iElementNameX = 2 * iMargin + m_pixmapSize.width() + iSpacing;
1225 int iElementNameY = iNameHeight == m_iMinimumHeaderHeight ?
1226 iMargin : iMargin + (m_iMinimumHeaderHeight - iNameHeight) / 2;
1227
1228 /* Simulate hyperlink hovering: */
1229 QPoint point = pEvent->pos().toPoint();
1230 bool fNameHovered = QRect(QPoint(iElementNameX, iElementNameY), m_nameSize).contains(point);
1231 if ( m_pSet->configurationAccessLevel() != ConfigurationAccessLevel_Null
1232 && m_fNameHovered != fNameHovered)
1233 {
1234 m_fNameHovered = fNameHovered;
1235 updateNameHoverLink();
1236 }
1237}
1238
1239void UIDetailsElement::updateNameHoverLink()
1240{
1241 if (m_fNameHovered)
1242 UICommon::setCursor(this, Qt::PointingHandCursor);
1243 else
1244 UICommon::unsetCursor(this);
1245 update();
1246}
1247
1248void UIDetailsElement::updateAnimationParameters()
1249{
1250 /* Recalculate animation parameters: */
1251 int iOpenedHeight = minimumHeightHintForElement(false);
1252 int iClosedHeight = minimumHeightHintForElement(true);
1253 int iAdditionalHeight = iOpenedHeight - iClosedHeight;
1254 if (m_fClosed)
1255 m_iAdditionalHeight = 0;
1256 else
1257 m_iAdditionalHeight = iAdditionalHeight;
1258 m_pButton->setAnimationRange(0, iAdditionalHeight);
1259}
1260
1261void UIDetailsElement::updateButtonVisibility()
1262{
1263 if (m_fHovered && !m_pButton->isVisible())
1264 m_pButton->show();
1265 else if (!m_fHovered && m_pButton->isVisible())
1266 m_pButton->hide();
1267}
1268
1269void UIDetailsElement::updateMinimumHeaderWidth()
1270{
1271 /* Prepare variables: */
1272 int iSpacing = data(ElementData_Spacing).toInt();
1273
1274 /* Update minimum-header-width: */
1275 m_iMinimumHeaderWidth = m_pixmapSize.width() +
1276 iSpacing + m_nameSize.width() +
1277 iSpacing + m_buttonSize.width();
1278}
1279
1280void UIDetailsElement::updateMinimumHeaderHeight()
1281{
1282 /* Update minimum-header-height: */
1283 m_iMinimumHeaderHeight = qMax(m_pixmapSize.height(), m_nameSize.height());
1284 m_iMinimumHeaderHeight = qMax(m_iMinimumHeaderHeight, m_buttonSize.height());
1285}
1286
1287void UIDetailsElement::paintBackground(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const
1288{
1289 /* Save painter: */
1290 pPainter->save();
1291
1292 /* Prepare variables: */
1293 const int iMargin = data(ElementData_Margin).toInt();
1294 const int iHeadHeight = 2 * iMargin + m_iMinimumHeaderHeight;
1295 const QRect optionRect = pOptions->rect;
1296 const QRect headRect = QRect(optionRect.topLeft(), QSize(optionRect.width(), iHeadHeight));
1297 const QRect fullRect = m_fAnimationRunning
1298 ? QRect(optionRect.topLeft(), QSize(optionRect.width(), iHeadHeight + m_iAdditionalHeight))
1299 : optionRect;
1300
1301 /* Acquire palette: */
1302 const QPalette pal = palette();
1303
1304 /* Paint default background: */
1305 const QColor defaultColor = pal.color(QPalette::Active, QPalette::Mid);
1306 const QColor dcTone1 = defaultColor.lighter(m_iDefaultToneFinal);
1307 const QColor dcTone2 = defaultColor.lighter(m_iDefaultToneStart);
1308 QLinearGradient gradientDefault(fullRect.topLeft(), fullRect.bottomLeft());
1309 gradientDefault.setColorAt(0, dcTone1);
1310 gradientDefault.setColorAt(1, dcTone2);
1311 pPainter->fillRect(fullRect, gradientDefault);
1312
1313 /* If element is hovered: */
1314 if (m_fHovered)
1315 {
1316 /* Paint hovered background: */
1317 const QColor hoveredColor = pal.color(QPalette::Active, QPalette::Highlight);
1318 QColor hcTone1 = hoveredColor.lighter(m_iHoverToneFinal);
1319 QColor hcTone2 = hoveredColor.lighter(m_iHoverToneStart);
1320 hcTone1.setAlpha(m_iAnimatedValue);
1321 hcTone2.setAlpha(m_iAnimatedValue);
1322 QLinearGradient gradientHovered(headRect.topLeft(), headRect.bottomLeft());
1323 gradientHovered.setColorAt(0, hcTone1);
1324 gradientHovered.setColorAt(1, hcTone2);
1325 pPainter->fillRect(headRect, gradientHovered);
1326 }
1327
1328 /* Restore painter: */
1329 pPainter->restore();
1330}
1331
1332void UIDetailsElement::paintFrame(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const
1333{
1334 /* Save painter: */
1335 pPainter->save();
1336
1337 /* Prepare variables: */
1338 const int iMargin = data(ElementData_Margin).toInt();
1339 const int iHeadHeight = 2 * iMargin + m_iMinimumHeaderHeight;
1340 const QRect optionRect = pOptions->rect;
1341 const QRect rectangle = m_fAnimationRunning
1342 ? QRect(optionRect.topLeft(), QSize(optionRect.width(), iHeadHeight + m_iAdditionalHeight))
1343 : optionRect;
1344
1345 /* Paint frame: */
1346 const QColor strokeColor = palette().color(QPalette::Active, QPalette::Mid).lighter(m_iDefaultToneStart);
1347 QPen pen(strokeColor);
1348 pen.setWidth(0);
1349 pPainter->setPen(pen);
1350 pPainter->drawLine(rectangle.topLeft(), rectangle.topRight());
1351 pPainter->drawLine(rectangle.bottomLeft(), rectangle.bottomRight());
1352 pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
1353 pPainter->drawLine(rectangle.topRight(), rectangle.bottomRight());
1354
1355 /* Restore painter: */
1356 pPainter->restore();
1357}
1358
1359void UIDetailsElement::paintElementInfo(QPainter *pPainter, const QStyleOptionGraphicsItem *) const
1360{
1361 /* Initialize some necessary variables: */
1362 const int iMargin = data(ElementData_Margin).toInt();
1363 const int iSpacing = data(ElementData_Spacing).toInt();
1364
1365 /* Calculate attributes: */
1366 const int iPixmapHeight = m_pixmapSize.height();
1367 const int iNameHeight = m_nameSize.height();
1368 const int iMaximumHeight = qMax(iPixmapHeight, iNameHeight);
1369
1370 /* Prepare color: */
1371 const QPalette pal = palette();
1372 const QColor buttonTextColor = pal.color(QPalette::Active, QPalette::ButtonText);
1373 const QColor linkTextColor = pal.color(QPalette::Active, QPalette::Link);
1374
1375 /* Paint pixmap: */
1376 int iElementPixmapX = 2 * iMargin;
1377 int iElementPixmapY = iPixmapHeight == iMaximumHeight ?
1378 iMargin : iMargin + (iMaximumHeight - iPixmapHeight) / 2;
1379 paintPixmap(/* Painter: */
1380 pPainter,
1381 /* Rectangle to paint in: */
1382 QRect(QPoint(iElementPixmapX, iElementPixmapY), m_pixmapSize),
1383 /* Pixmap to paint: */
1384 m_pixmap);
1385
1386 /* Paint name: */
1387 int iMachineNameX = iElementPixmapX +
1388 m_pixmapSize.width() +
1389 iSpacing;
1390 int iMachineNameY = iNameHeight == iMaximumHeight ?
1391 iMargin : iMargin + (iMaximumHeight - iNameHeight) / 2;
1392 paintText(/* Painter: */
1393 pPainter,
1394 /* Rectangle to paint in: */
1395 QPoint(iMachineNameX, iMachineNameY),
1396 /* Font to paint text: */
1397 m_nameFont,
1398 /* Paint device: */
1399 model()->paintDevice(),
1400 /* Text to paint: */
1401 m_strName,
1402 /* Name hovered? */
1403 m_fNameHovered ? linkTextColor : buttonTextColor);
1404}
1405
1406/* static */
1407void UIDetailsElement::paintPixmap(QPainter *pPainter, const QRect &rect, const QPixmap &pixmap)
1408{
1409 pPainter->drawPixmap(rect, pixmap);
1410}
1411
1412/* static */
1413void UIDetailsElement::paintText(QPainter *pPainter, QPoint point,
1414 const QFont &font, QPaintDevice *pPaintDevice,
1415 const QString &strText, const QColor &color)
1416{
1417 /* Prepare variables: */
1418 QFontMetrics fm(font, pPaintDevice);
1419 point += QPoint(0, fm.ascent());
1420
1421 /* Draw text: */
1422 pPainter->save();
1423 pPainter->setFont(font);
1424 pPainter->setPen(color);
1425 pPainter->drawText(point, strText);
1426 pPainter->restore();
1427}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette