VirtualBox

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

Last change on this file since 83149 was 83149, checked in by vboxsync, 5 years ago

FE/Qt: bugref:9653: VirtualBox Manager: Details pane: Make set and element items aware of chooser item type.

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

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