VirtualBox

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

Last change on this file since 80073 was 80073, checked in by vboxsync, 6 years ago

FE/Qt: bugref:7720: VirtualBox Manager: Details pane: Possibility to edit VM graphics controller type on-the-fly.

  • 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: 35.0 KB
Line 
1/* $Id: UIDetailsElement.cpp 80073 2019-07-31 11:54:13Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIDetailsElement class implementation.
4 */
5
6/*
7 * Copyright (C) 2012-2019 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 <QGraphicsSceneMouseEvent>
20#include <QGraphicsView>
21#include <QPropertyAnimation>
22#include <QSignalTransition>
23#include <QStateMachine>
24#include <QStyleOptionGraphicsItem>
25
26/* GUI includes: */
27#include "QIDialogContainer.h"
28#include "UIActionPool.h"
29#include "UIBaseMemoryEditor.h"
30#include "UIBootOrderEditor.h"
31#include "UICommon.h"
32#include "UIConverter.h"
33#include "UIDetailsElement.h"
34#include "UIDetailsSet.h"
35#include "UIDetailsModel.h"
36#include "UIExtraDataManager.h"
37#include "UIGraphicsControllerEditor.h"
38#include "UIGraphicsRotatorButton.h"
39#include "UIGraphicsTextPane.h"
40#include "UIIconPool.h"
41#include "UIMachineAttributeSetter.h"
42#include "UINameAndSystemEditor.h"
43#include "UIVideoMemoryEditor.h"
44#include "UIVirtualBoxManager.h"
45
46
47/** Known anchor roles. */
48enum AnchorRole
49{
50 AnchorRole_Invalid,
51 AnchorRole_MachineName,
52 AnchorRole_MachineLocation,
53 AnchorRole_OSType,
54 AnchorRole_BaseMemory,
55 AnchorRole_BootOrder,
56 AnchorRole_VideoMemory,
57 AnchorRole_GraphicsControllerType,
58 AnchorRole_Storage,
59};
60
61
62UIDetailsElement::UIDetailsElement(UIDetailsSet *pParent, DetailsElementType enmType, bool fOpened)
63 : UIDetailsItem(pParent)
64 , m_pSet(pParent)
65 , m_enmType(enmType)
66#ifdef VBOX_WS_MAC
67 , m_iDefaultToneStart(145)
68 , m_iDefaultToneFinal(155)
69 , m_iHoverToneStart(115)
70 , m_iHoverToneFinal(125)
71#else
72 , m_iDefaultToneStart(160)
73 , m_iDefaultToneFinal(190)
74 , m_iHoverToneStart(160)
75 , m_iHoverToneFinal(190)
76#endif
77 , m_fHovered(false)
78 , m_fNameHovered(false)
79 , m_pHoveringMachine(0)
80 , m_pHoveringAnimationForward(0)
81 , m_pHoveringAnimationBackward(0)
82 , m_iAnimationDuration(300)
83 , m_iDefaultValue(0)
84 , m_iHoveredValue(255)
85 , m_iAnimatedValue(m_iDefaultValue)
86 , m_pButton(0)
87 , m_fClosed(!fOpened)
88 , m_fAnimationRunning(false)
89 , m_iAdditionalHeight(0)
90 , m_pTextPane(0)
91 , m_iMinimumHeaderWidth(0)
92 , m_iMinimumHeaderHeight(0)
93{
94 /* Prepare element: */
95 prepareElement();
96 /* Prepare button: */
97 prepareButton();
98 /* Prepare text-pane: */
99 prepareTextPane();
100
101 /* Setup size-policy: */
102 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
103
104 /* Add item to the parent: */
105 AssertMsg(parentItem(), ("No parent set for details element!"));
106 parentItem()->addItem(this);
107}
108
109UIDetailsElement::~UIDetailsElement()
110{
111 /* Remove item from the parent: */
112 AssertMsg(parentItem(), ("No parent set for details element!"));
113 parentItem()->removeItem(this);
114}
115
116void UIDetailsElement::setText(const UITextTable &text)
117{
118 /* Pass text to text-pane: */
119 m_pTextPane->setText(text);
120}
121
122UITextTable &UIDetailsElement::text() const
123{
124 /* Retrieve text from text-pane: */
125 return m_pTextPane->text();
126}
127
128void UIDetailsElement::close(bool fAnimated /* = true */)
129{
130 m_pButton->setToggled(false, fAnimated);
131}
132
133void UIDetailsElement::open(bool fAnimated /* = true */)
134{
135 m_pButton->setToggled(true, fAnimated);
136}
137
138void UIDetailsElement::markAnimationFinished()
139{
140 /* Mark animation as non-running: */
141 m_fAnimationRunning = false;
142
143 /* Recursively update size-hint: */
144 updateGeometry();
145 /* Repaint: */
146 update();
147}
148
149void UIDetailsElement::updateAppearance()
150{
151 /* Reset name hover state: */
152 m_fNameHovered = false;
153 updateNameHoverLink();
154
155 /* Update anchor role restrictions: */
156 ConfigurationAccessLevel cal = m_pSet->configurationAccessLevel();
157 m_pTextPane->setAnchorRoleRestricted("#machine_name", cal != ConfigurationAccessLevel_Full);
158 m_pTextPane->setAnchorRoleRestricted("#machine_location", cal != ConfigurationAccessLevel_Full);
159 m_pTextPane->setAnchorRoleRestricted("#os_type", cal != ConfigurationAccessLevel_Full);
160 m_pTextPane->setAnchorRoleRestricted("#base_memory", cal != ConfigurationAccessLevel_Full);
161 m_pTextPane->setAnchorRoleRestricted("#boot_order", cal != ConfigurationAccessLevel_Full);
162 m_pTextPane->setAnchorRoleRestricted("#video_memory", cal != ConfigurationAccessLevel_Full);
163 m_pTextPane->setAnchorRoleRestricted("#graphics_controller_type", cal != ConfigurationAccessLevel_Full);
164 m_pTextPane->setAnchorRoleRestricted("#mount", cal == ConfigurationAccessLevel_Null);
165 m_pTextPane->setAnchorRoleRestricted("#attach", cal != ConfigurationAccessLevel_Full);
166}
167
168int UIDetailsElement::minimumWidthHint() const
169{
170 /* Prepare variables: */
171 int iMargin = data(ElementData_Margin).toInt();
172 int iMinimumWidthHint = 0;
173
174 /* Maximum width: */
175 iMinimumWidthHint = qMax(m_iMinimumHeaderWidth, (int)m_pTextPane->minimumSizeHint().width());
176
177 /* And 4 margins: 2 left and 2 right: */
178 iMinimumWidthHint += 4 * iMargin;
179
180 /* Return result: */
181 return iMinimumWidthHint;
182}
183
184int UIDetailsElement::minimumHeightHint() const
185{
186 return minimumHeightHintForElement(m_fClosed);
187}
188
189void UIDetailsElement::showEvent(QShowEvent *pEvent)
190{
191 /* Call to base-class: */
192 UIDetailsItem::showEvent(pEvent);
193
194 /* Update icon: */
195 updateIcon();
196}
197
198void UIDetailsElement::resizeEvent(QGraphicsSceneResizeEvent*)
199{
200 /* Update layout: */
201 updateLayout();
202}
203
204void UIDetailsElement::hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent)
205{
206 /* Update hover state: */
207 if (!m_fHovered)
208 {
209 m_fHovered = true;
210 emit sigHoverEnter();
211 }
212
213 /* Update name-hover state: */
214 handleHoverEvent(pEvent);
215}
216
217void UIDetailsElement::hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent)
218{
219 /* Update hover state: */
220 if (m_fHovered)
221 {
222 m_fHovered = false;
223 emit sigHoverLeave();
224 }
225
226 /* Update name-hover state: */
227 handleHoverEvent(pEvent);
228}
229
230void UIDetailsElement::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
231{
232 /* Only for hovered header: */
233 if (!m_fNameHovered)
234 return;
235
236 /* Process link click: */
237 pEvent->accept();
238 QString strCategory;
239 if (m_enmType >= DetailsElementType_General &&
240 m_enmType < DetailsElementType_Description)
241 strCategory = QString("#%1").arg(gpConverter->toInternalString(m_enmType));
242 else if (m_enmType == DetailsElementType_Description)
243 strCategory = QString("#%1%%mTeDescription").arg(gpConverter->toInternalString(m_enmType));
244 emit sigLinkClicked(strCategory, QString(), machine().GetId());
245}
246
247void UIDetailsElement::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *pEvent)
248{
249 /* Only for left-button: */
250 if (pEvent->button() != Qt::LeftButton)
251 return;
252
253 /* Process left-button double-click: */
254 emit sigToggleElement(m_enmType, isClosed());
255}
256
257void UIDetailsElement::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *)
258{
259 /* Update button visibility: */
260 updateButtonVisibility();
261
262 /* Paint background: */
263 paintBackground(pPainter, pOptions);
264 /* Paint frame: */
265 paintFrame(pPainter, pOptions);
266 /* Paint element info: */
267 paintElementInfo(pPainter, pOptions);
268}
269
270QString UIDetailsElement::description() const
271{
272 return tr("%1 details", "like 'General details' or 'Storage details'").arg(m_strName);
273}
274
275const CMachine &UIDetailsElement::machine()
276{
277 return m_pSet->machine();
278}
279
280void UIDetailsElement::setName(const QString &strName)
281{
282 /* Cache name: */
283 m_strName = strName;
284 QFontMetrics fm(m_nameFont, model()->paintDevice());
285 m_nameSize = QSize(fm.width(m_strName), fm.height());
286
287 /* Update linked values: */
288 updateMinimumHeaderWidth();
289 updateMinimumHeaderHeight();
290}
291
292void UIDetailsElement::setAdditionalHeight(int iAdditionalHeight)
293{
294 /* Cache new value: */
295 m_iAdditionalHeight = iAdditionalHeight;
296 /* Update layout: */
297 updateLayout();
298 /* Repaint: */
299 update();
300}
301
302QVariant UIDetailsElement::data(int iKey) const
303{
304 /* Provide other members with required data: */
305 switch (iKey)
306 {
307 /* Hints: */
308 case ElementData_Margin: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
309 case ElementData_Spacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
310 /* Default: */
311 default: break;
312 }
313 return QVariant();
314}
315
316void UIDetailsElement::addItem(UIDetailsItem*)
317{
318 AssertMsgFailed(("Details element do NOT support children!"));
319}
320
321void UIDetailsElement::removeItem(UIDetailsItem*)
322{
323 AssertMsgFailed(("Details element do NOT support children!"));
324}
325
326QList<UIDetailsItem*> UIDetailsElement::items(UIDetailsItemType) const
327{
328 AssertMsgFailed(("Details element do NOT support children!"));
329 return QList<UIDetailsItem*>();
330}
331
332bool UIDetailsElement::hasItems(UIDetailsItemType) const
333{
334 AssertMsgFailed(("Details element do NOT support children!"));
335 return false;
336}
337
338void UIDetailsElement::clearItems(UIDetailsItemType)
339{
340 AssertMsgFailed(("Details element do NOT support children!"));
341}
342
343void UIDetailsElement::updateLayout()
344{
345 /* Prepare variables: */
346 QSize size = geometry().size().toSize();
347 int iMargin = data(ElementData_Margin).toInt();
348
349 /* Layout button: */
350 int iButtonWidth = m_buttonSize.width();
351 int iButtonHeight = m_buttonSize.height();
352 int iButtonX = size.width() - 2 * iMargin - iButtonWidth;
353 int iButtonY = iButtonHeight == m_iMinimumHeaderHeight ? iMargin :
354 iMargin + (m_iMinimumHeaderHeight - iButtonHeight) / 2;
355 m_pButton->setPos(iButtonX, iButtonY);
356
357 /* If closed: */
358 if (isClosed())
359 {
360 /* Hide text-pane if still visible: */
361 if (m_pTextPane->isVisible())
362 m_pTextPane->hide();
363 }
364 /* If opened: */
365 else
366 {
367 /* Layout text-pane: */
368 int iTextPaneX = 2 * iMargin;
369 int iTextPaneY = iMargin + m_iMinimumHeaderHeight + 2 * iMargin;
370 m_pTextPane->setPos(iTextPaneX, iTextPaneY);
371 m_pTextPane->resize(size.width() - 4 * iMargin,
372 size.height() - 4 * iMargin - m_iMinimumHeaderHeight);
373 /* Show text-pane if still invisible and animation finished: */
374 if (!m_pTextPane->isVisible() && !isAnimationRunning())
375 m_pTextPane->show();
376 }
377}
378
379int UIDetailsElement::minimumHeightHintForElement(bool fClosed) const
380{
381 /* Prepare variables: */
382 int iMargin = data(ElementData_Margin).toInt();
383 int iMinimumHeightHint = 0;
384
385 /* Two margins: */
386 iMinimumHeightHint += 2 * iMargin;
387
388 /* Header height: */
389 iMinimumHeightHint += m_iMinimumHeaderHeight;
390
391 /* Element is opened? */
392 if (!fClosed)
393 {
394 /* Add text height: */
395 if (!m_pTextPane->isEmpty())
396 iMinimumHeightHint += 2 * iMargin + (int)m_pTextPane->minimumSizeHint().height();
397 }
398
399 /* Additional height during animation: */
400 if (m_fAnimationRunning)
401 iMinimumHeightHint += m_iAdditionalHeight;
402
403 /* Return value: */
404 return iMinimumHeightHint;
405}
406
407void UIDetailsElement::sltHandleWindowRemapped()
408{
409 /* Update icon: */
410 updateIcon();
411}
412
413void UIDetailsElement::sltToggleButtonClicked()
414{
415 emit sigToggleElement(m_enmType, isClosed());
416}
417
418void UIDetailsElement::sltElementToggleStart()
419{
420 /* Mark animation running: */
421 m_fAnimationRunning = true;
422
423 /* Setup animation: */
424 updateAnimationParameters();
425
426 /* Invert toggle-state: */
427 m_fClosed = !m_fClosed;
428}
429
430void UIDetailsElement::sltElementToggleFinish(bool fToggled)
431{
432 /* Update toggle-state: */
433 m_fClosed = !fToggled;
434
435 /* Notify about finishing: */
436 emit sigToggleElementFinished();
437}
438
439void UIDetailsElement::sltHandleAnchorClicked(const QString &strAnchor)
440{
441 /* Compose a map of known anchor roles: */
442 QMap<QString, AnchorRole> roles;
443 roles["#machine_name"] = AnchorRole_MachineName;
444 roles["#machine_location"] = AnchorRole_MachineLocation;
445 roles["#os_type"] = AnchorRole_OSType;
446 roles["#base_memory"] = AnchorRole_BaseMemory;
447 roles["#boot_order"] = AnchorRole_BootOrder;
448 roles["#video_memory"] = AnchorRole_VideoMemory;
449 roles["#graphics_controller_type"] = AnchorRole_GraphicsControllerType;
450 roles["#mount"] = AnchorRole_Storage;
451 roles["#attach"] = AnchorRole_Storage;
452
453 /* Current anchor role: */
454 const QString strRole = strAnchor.section(',', 0, 0);
455 const QString strData = strAnchor.section(',', 1);
456
457 /* Handle known anchor roles: */
458 const AnchorRole enmRole = roles.value(strRole, AnchorRole_Invalid);
459 switch (enmRole)
460 {
461 case AnchorRole_MachineName:
462 case AnchorRole_MachineLocation:
463 case AnchorRole_OSType:
464 {
465 /* Prepare popup: */
466 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
467 if (pPopup)
468 {
469 /* Prepare editor: */
470 UINameAndSystemEditor *pEditor =
471 new UINameAndSystemEditor(pPopup,
472 enmRole == AnchorRole_MachineName /* choose name? */,
473 enmRole == AnchorRole_MachineLocation /* choose path? */,
474 enmRole == AnchorRole_OSType /* choose type? */);
475 if (pEditor)
476 {
477 switch (enmRole)
478 {
479 case AnchorRole_MachineName: pEditor->setName(strData.section(',', 0, 0)); break;
480 case AnchorRole_MachineLocation: pEditor->setPath(strData.section(',', 0, 0)); break;
481 case AnchorRole_OSType: pEditor->setTypeId(strData.section(',', 0, 0)); break;
482 default: break;
483 }
484
485 /* Add to popup: */
486 pPopup->setWidget(pEditor);
487 }
488
489 /* Adjust popup geometry: */
490 pPopup->move(QCursor::pos());
491 pPopup->adjustSize();
492
493 // WORKAROUND:
494 // On Windows, Tool dialogs aren't activated by default by some reason.
495 // So we have created sltActivateWindow wrapping actual activateWindow
496 // to fix that annoying issue.
497 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
498 /* Execute popup, change machine name if confirmed: */
499 if (pPopup->exec() == QDialog::Accepted)
500 {
501 switch (enmRole)
502 {
503 case AnchorRole_MachineName: setMachineAttribute(machine(), MachineAttribute_Name, QVariant::fromValue(pEditor->name())); break;
504 case AnchorRole_MachineLocation: setMachineAttribute(machine(), MachineAttribute_Location, QVariant::fromValue(pEditor->path())); break;
505 case AnchorRole_OSType: setMachineAttribute(machine(), MachineAttribute_OSType, QVariant::fromValue(pEditor->typeId())); break;
506 default: break;
507 }
508 }
509
510 /* Delete popup: */
511 delete pPopup;
512 }
513 break;
514 }
515 case AnchorRole_BaseMemory:
516 {
517 /* Prepare popup: */
518 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
519 if (pPopup)
520 {
521 /* Prepare editor: */
522 UIBaseMemoryEditor *pEditor = new UIBaseMemoryEditor(pPopup, true /* with label */);
523 if (pEditor)
524 {
525 pEditor->setValue(strData.section(',', 0, 0).toInt());
526 connect(pEditor, &UIBaseMemoryEditor::sigValidChanged,
527 pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
528 pPopup->setWidget(pEditor);
529 }
530
531 /* Adjust popup geometry: */
532 pPopup->move(QCursor::pos());
533 pPopup->adjustSize();
534
535 // WORKAROUND:
536 // On Windows, Tool dialogs aren't activated by default by some reason.
537 // So we have created sltActivateWindow wrapping actual activateWindow
538 // to fix that annoying issue.
539 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
540 /* Execute popup, change machine name if confirmed: */
541 if (pPopup->exec() == QDialog::Accepted)
542 setMachineAttribute(machine(), MachineAttribute_BaseMemory, QVariant::fromValue(pEditor->value()));
543
544 /* Delete popup: */
545 delete pPopup;
546 }
547 break;
548 }
549 case AnchorRole_BootOrder:
550 {
551 /* Prepare popup: */
552 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
553 if (pPopup)
554 {
555 /* Prepare editor: */
556 UIBootOrderEditor *pEditor = new UIBootOrderEditor(pPopup, true /* with label */);
557 if (pEditor)
558 {
559 pEditor->setValue(bootItemsFromSerializedString(strData.section(',', 0, 0)));
560 pPopup->setWidget(pEditor);
561 }
562
563 /* Adjust popup geometry: */
564 pPopup->move(QCursor::pos());
565 pPopup->adjustSize();
566
567 // WORKAROUND:
568 // On Windows, Tool dialogs aren't activated by default by some reason.
569 // So we have created sltActivateWindow wrapping actual activateWindow
570 // to fix that annoying issue.
571 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
572 /* Execute popup, change machine name if confirmed: */
573 if (pPopup->exec() == QDialog::Accepted)
574 setMachineAttribute(machine(), MachineAttribute_BootOrder, QVariant::fromValue(pEditor->value()));
575
576 /* Delete popup: */
577 delete pPopup;
578 }
579 break;
580 }
581 case AnchorRole_VideoMemory:
582 {
583 /* Prepare popup: */
584 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
585 if (pPopup)
586 {
587 /* Prepare editor: */
588 UIVideoMemoryEditor *pEditor = new UIVideoMemoryEditor(pPopup, true /* with label */);
589 if (pEditor)
590 {
591 pEditor->setValue(strData.section(',', 0, 0).toInt());
592 connect(pEditor, &UIVideoMemoryEditor::sigValidChanged,
593 pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
594 pPopup->setWidget(pEditor);
595 }
596
597 /* Adjust popup geometry: */
598 pPopup->move(QCursor::pos());
599 pPopup->adjustSize();
600
601 // WORKAROUND:
602 // On Windows, Tool dialogs aren't activated by default by some reason.
603 // So we have created sltActivateWindow wrapping actual activateWindow
604 // to fix that annoying issue.
605 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
606 /* Execute popup, change machine name if confirmed: */
607 if (pPopup->exec() == QDialog::Accepted)
608 setMachineAttribute(machine(), MachineAttribute_VideoMemory, QVariant::fromValue(pEditor->value()));
609
610 /* Delete popup: */
611 delete pPopup;
612 }
613 break;
614 }
615 case AnchorRole_GraphicsControllerType:
616 {
617 /* Prepare popup: */
618 QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
619 if (pPopup)
620 {
621 /* Prepare editor: */
622 UIGraphicsControllerEditor *pEditor = new UIGraphicsControllerEditor(pPopup, true /* with label */);
623 if (pEditor)
624 {
625 pEditor->setValue(static_cast<KGraphicsControllerType>(strData.section(',', 0, 0).toInt()));
626 pPopup->setWidget(pEditor);
627 }
628
629 /* Adjust popup geometry: */
630 pPopup->move(QCursor::pos());
631 pPopup->adjustSize();
632
633 // WORKAROUND:
634 // On Windows, Tool dialogs aren't activated by default by some reason.
635 // So we have created sltActivateWindow wrapping actual activateWindow
636 // to fix that annoying issue.
637 QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
638 /* Execute popup, change machine name if confirmed: */
639 if (pPopup->exec() == QDialog::Accepted)
640 setMachineAttribute(machine(), MachineAttribute_GraphicsControllerType, QVariant::fromValue(pEditor->value()));
641
642 /* Delete popup: */
643 delete pPopup;
644 }
645 break;
646 }
647 case AnchorRole_Storage:
648 {
649 /* Prepare storage-menu: */
650 UIMenu menu;
651 menu.setShowToolTip(true);
652
653 /* Storage-controller name: */
654 QString strControllerName = strData.section(',', 0, 0);
655 /* Storage-slot: */
656 StorageSlot storageSlot = gpConverter->fromString<StorageSlot>(strData.section(',', 1));
657
658 /* Fill storage-menu: */
659 uiCommon().prepareStorageMenu(menu, this, SLOT(sltMountStorageMedium()),
660 machine(), strControllerName, storageSlot);
661
662 /* Exec menu: */
663 menu.exec(QCursor::pos());
664 break;
665 }
666 default:
667 break;
668 }
669}
670
671void UIDetailsElement::sltMountStorageMedium()
672{
673 /* Sender action: */
674 QAction *pAction = qobject_cast<QAction*>(sender());
675 AssertMsgReturnVoid(pAction, ("This slot should only be called by menu action!\n"));
676
677 /* Current mount-target: */
678 const UIMediumTarget target = pAction->data().value<UIMediumTarget>();
679
680 /* Update current machine mount-target: */
681 uiCommon().updateMachineStorage(machine(), target);
682}
683
684void UIDetailsElement::prepareElement()
685{
686 /* Initialization: */
687 m_nameFont = font();
688 m_nameFont.setWeight(QFont::Bold);
689 m_textFont = font();
690
691 /* Update icon: */
692 updateIcon();
693
694 /* Create hovering animation machine: */
695 m_pHoveringMachine = new QStateMachine(this);
696 if (m_pHoveringMachine)
697 {
698 /* Create 'default' state: */
699 QState *pStateDefault = new QState(m_pHoveringMachine);
700 /* Create 'hovered' state: */
701 QState *pStateHovered = new QState(m_pHoveringMachine);
702
703 /* Configure 'default' state: */
704 if (pStateDefault)
705 {
706 /* When we entering default state => we assigning animatedValue to m_iDefaultValue: */
707 pStateDefault->assignProperty(this, "animatedValue", m_iDefaultValue);
708
709 /* Add state transition: */
710 QSignalTransition *pDefaultToHovered = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateHovered);
711 if (pDefaultToHovered)
712 {
713 /* Create forward animation: */
714 m_pHoveringAnimationForward = new QPropertyAnimation(this, "animatedValue", this);
715 if (m_pHoveringAnimationForward)
716 {
717 m_pHoveringAnimationForward->setDuration(m_iAnimationDuration);
718 m_pHoveringAnimationForward->setStartValue(m_iDefaultValue);
719 m_pHoveringAnimationForward->setEndValue(m_iHoveredValue);
720
721 /* Add to transition: */
722 pDefaultToHovered->addAnimation(m_pHoveringAnimationForward);
723 }
724 }
725 }
726
727 /* Configure 'hovered' state: */
728 if (pStateHovered)
729 {
730 /* When we entering hovered state => we assigning animatedValue to m_iHoveredValue: */
731 pStateHovered->assignProperty(this, "animatedValue", m_iHoveredValue);
732
733 /* Add state transition: */
734 QSignalTransition *pHoveredToDefault = pStateHovered->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
735 if (pHoveredToDefault)
736 {
737 /* Create backward animation: */
738 m_pHoveringAnimationBackward = new QPropertyAnimation(this, "animatedValue", this);
739 if (m_pHoveringAnimationBackward)
740 {
741 m_pHoveringAnimationBackward->setDuration(m_iAnimationDuration);
742 m_pHoveringAnimationBackward->setStartValue(m_iHoveredValue);
743 m_pHoveringAnimationBackward->setEndValue(m_iDefaultValue);
744
745 /* Add to transition: */
746 pHoveredToDefault->addAnimation(m_pHoveringAnimationBackward);
747 }
748 }
749 }
750
751 /* Initial state is 'default': */
752 m_pHoveringMachine->setInitialState(pStateDefault);
753 /* Start state-machine: */
754 m_pHoveringMachine->start();
755 }
756
757 /* Configure connections: */
758 connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
759 this, &UIDetailsElement::sltHandleWindowRemapped);
760 connect(this, SIGNAL(sigToggleElement(DetailsElementType, bool)),
761 model(), SLOT(sltToggleElements(DetailsElementType, bool)));
762 connect(this, SIGNAL(sigLinkClicked(const QString&, const QString&, const QUuid &)),
763 model(), SIGNAL(sigLinkClicked(const QString&, const QString&, const QUuid &)));
764}
765
766void UIDetailsElement::prepareButton()
767{
768 /* Setup toggle-button: */
769 m_pButton = new UIGraphicsRotatorButton(this, "additionalHeight", !m_fClosed, true /* reflected */);
770 m_pButton->setAutoHandleButtonClick(false);
771 connect(m_pButton, SIGNAL(sigButtonClicked()), this, SLOT(sltToggleButtonClicked()));
772 connect(m_pButton, SIGNAL(sigRotationStart()), this, SLOT(sltElementToggleStart()));
773 connect(m_pButton, SIGNAL(sigRotationFinish(bool)), this, SLOT(sltElementToggleFinish(bool)));
774 m_buttonSize = m_pButton->minimumSizeHint().toSize();
775}
776
777void UIDetailsElement::prepareTextPane()
778{
779 /* Create text-pane: */
780 m_pTextPane = new UIGraphicsTextPane(this, model()->paintDevice());
781 connect(m_pTextPane, SIGNAL(sigGeometryChanged()), this, SLOT(sltUpdateGeometry()));
782 connect(m_pTextPane, SIGNAL(sigAnchorClicked(const QString&)), this, SLOT(sltHandleAnchorClicked(const QString&)));
783}
784
785void UIDetailsElement::updateIcon()
786{
787 /* Prepare whole icon first of all: */
788 const QIcon icon = gpConverter->toIcon(elementType());
789
790 /* Cache icon: */
791 if (icon.isNull())
792 {
793 /* No icon provided: */
794 m_pixmapSize = QSize();
795 m_pixmap = QPixmap();
796 }
797 else
798 {
799 /* Determine default icon size: */
800 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
801 m_pixmapSize = QSize(iIconMetric, iIconMetric);
802 /* Acquire the icon of corresponding size (taking top-level widget DPI into account): */
803 m_pixmap = icon.pixmap(gpManager->windowHandle(), m_pixmapSize);
804 }
805
806 /* Update linked values: */
807 updateMinimumHeaderWidth();
808 updateMinimumHeaderHeight();
809}
810
811void UIDetailsElement::handleHoverEvent(QGraphicsSceneHoverEvent *pEvent)
812{
813 /* Not for 'preview' element type: */
814 if (m_enmType == DetailsElementType_Preview)
815 return;
816
817 /* Prepare variables: */
818 int iMargin = data(ElementData_Margin).toInt();
819 int iSpacing = data(ElementData_Spacing).toInt();
820 int iNameHeight = m_nameSize.height();
821 int iElementNameX = 2 * iMargin + m_pixmapSize.width() + iSpacing;
822 int iElementNameY = iNameHeight == m_iMinimumHeaderHeight ?
823 iMargin : iMargin + (m_iMinimumHeaderHeight - iNameHeight) / 2;
824
825 /* Simulate hyperlink hovering: */
826 QPoint point = pEvent->pos().toPoint();
827 bool fNameHovered = QRect(QPoint(iElementNameX, iElementNameY), m_nameSize).contains(point);
828 if ( m_pSet->configurationAccessLevel() != ConfigurationAccessLevel_Null
829 && m_fNameHovered != fNameHovered)
830 {
831 m_fNameHovered = fNameHovered;
832 updateNameHoverLink();
833 }
834}
835
836void UIDetailsElement::updateNameHoverLink()
837{
838 if (m_fNameHovered)
839 UICommon::setCursor(this, Qt::PointingHandCursor);
840 else
841 UICommon::unsetCursor(this);
842 update();
843}
844
845void UIDetailsElement::updateAnimationParameters()
846{
847 /* Recalculate animation parameters: */
848 int iOpenedHeight = minimumHeightHintForElement(false);
849 int iClosedHeight = minimumHeightHintForElement(true);
850 int iAdditionalHeight = iOpenedHeight - iClosedHeight;
851 if (m_fClosed)
852 m_iAdditionalHeight = 0;
853 else
854 m_iAdditionalHeight = iAdditionalHeight;
855 m_pButton->setAnimationRange(0, iAdditionalHeight);
856}
857
858void UIDetailsElement::updateButtonVisibility()
859{
860 if (m_fHovered && !m_pButton->isVisible())
861 m_pButton->show();
862 else if (!m_fHovered && m_pButton->isVisible())
863 m_pButton->hide();
864}
865
866void UIDetailsElement::updateMinimumHeaderWidth()
867{
868 /* Prepare variables: */
869 int iSpacing = data(ElementData_Spacing).toInt();
870
871 /* Update minimum-header-width: */
872 m_iMinimumHeaderWidth = m_pixmapSize.width() +
873 iSpacing + m_nameSize.width() +
874 iSpacing + m_buttonSize.width();
875}
876
877void UIDetailsElement::updateMinimumHeaderHeight()
878{
879 /* Update minimum-header-height: */
880 m_iMinimumHeaderHeight = qMax(m_pixmapSize.height(), m_nameSize.height());
881 m_iMinimumHeaderHeight = qMax(m_iMinimumHeaderHeight, m_buttonSize.height());
882}
883
884void UIDetailsElement::paintBackground(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const
885{
886 /* Save painter: */
887 pPainter->save();
888
889 /* Prepare variables: */
890 const int iMargin = data(ElementData_Margin).toInt();
891 const int iHeadHeight = 2 * iMargin + m_iMinimumHeaderHeight;
892 const QRect optionRect = pOptions->rect;
893 const QRect headRect = QRect(optionRect.topLeft(), QSize(optionRect.width(), iHeadHeight));
894 const QRect fullRect = m_fAnimationRunning
895 ? QRect(optionRect.topLeft(), QSize(optionRect.width(), iHeadHeight + m_iAdditionalHeight))
896 : optionRect;
897
898 /* Acquire palette: */
899 const QPalette pal = palette();
900
901 /* Paint default background: */
902 const QColor defaultColor = pal.color(QPalette::Active, QPalette::Mid);
903 const QColor dcTone1 = defaultColor.lighter(m_iDefaultToneFinal);
904 const QColor dcTone2 = defaultColor.lighter(m_iDefaultToneStart);
905 QLinearGradient gradientDefault(fullRect.topLeft(), fullRect.bottomLeft());
906 gradientDefault.setColorAt(0, dcTone1);
907 gradientDefault.setColorAt(1, dcTone2);
908 pPainter->fillRect(fullRect, gradientDefault);
909
910 /* If element is hovered: */
911 if (m_fHovered)
912 {
913 /* Paint hovered background: */
914 const QColor hoveredColor = pal.color(QPalette::Active, QPalette::Highlight);
915 QColor hcTone1 = hoveredColor.lighter(m_iHoverToneFinal);
916 QColor hcTone2 = hoveredColor.lighter(m_iHoverToneStart);
917 hcTone1.setAlpha(m_iAnimatedValue);
918 hcTone2.setAlpha(m_iAnimatedValue);
919 QLinearGradient gradientHovered(headRect.topLeft(), headRect.bottomLeft());
920 gradientHovered.setColorAt(0, hcTone1);
921 gradientHovered.setColorAt(1, hcTone2);
922 pPainter->fillRect(headRect, gradientHovered);
923 }
924
925 /* Restore painter: */
926 pPainter->restore();
927}
928
929void UIDetailsElement::paintFrame(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const
930{
931 /* Save painter: */
932 pPainter->save();
933
934 /* Prepare variables: */
935 const int iMargin = data(ElementData_Margin).toInt();
936 const int iHeadHeight = 2 * iMargin + m_iMinimumHeaderHeight;
937 const QRect optionRect = pOptions->rect;
938 const QRect rectangle = m_fAnimationRunning
939 ? QRect(optionRect.topLeft(), QSize(optionRect.width(), iHeadHeight + m_iAdditionalHeight))
940 : optionRect;
941
942 /* Paint frame: */
943 const QColor strokeColor = palette().color(QPalette::Active, QPalette::Mid).lighter(m_iDefaultToneStart);
944 QPen pen(strokeColor);
945 pen.setWidth(0);
946 pPainter->setPen(pen);
947 pPainter->drawLine(rectangle.topLeft(), rectangle.topRight());
948 pPainter->drawLine(rectangle.bottomLeft(), rectangle.bottomRight());
949 pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
950 pPainter->drawLine(rectangle.topRight(), rectangle.bottomRight());
951
952 /* Restore painter: */
953 pPainter->restore();
954}
955
956void UIDetailsElement::paintElementInfo(QPainter *pPainter, const QStyleOptionGraphicsItem *) const
957{
958 /* Initialize some necessary variables: */
959 const int iMargin = data(ElementData_Margin).toInt();
960 const int iSpacing = data(ElementData_Spacing).toInt();
961
962 /* Calculate attributes: */
963 const int iPixmapHeight = m_pixmapSize.height();
964 const int iNameHeight = m_nameSize.height();
965 const int iMaximumHeight = qMax(iPixmapHeight, iNameHeight);
966
967 /* Prepare color: */
968 const QPalette pal = palette();
969 const QColor buttonTextColor = pal.color(QPalette::Active, QPalette::ButtonText);
970 const QColor linkTextColor = pal.color(QPalette::Active, QPalette::Link);
971
972 /* Paint pixmap: */
973 int iElementPixmapX = 2 * iMargin;
974 int iElementPixmapY = iPixmapHeight == iMaximumHeight ?
975 iMargin : iMargin + (iMaximumHeight - iPixmapHeight) / 2;
976 paintPixmap(/* Painter: */
977 pPainter,
978 /* Rectangle to paint in: */
979 QRect(QPoint(iElementPixmapX, iElementPixmapY), m_pixmapSize),
980 /* Pixmap to paint: */
981 m_pixmap);
982
983 /* Paint name: */
984 int iMachineNameX = iElementPixmapX +
985 m_pixmapSize.width() +
986 iSpacing;
987 int iMachineNameY = iNameHeight == iMaximumHeight ?
988 iMargin : iMargin + (iMaximumHeight - iNameHeight) / 2;
989 paintText(/* Painter: */
990 pPainter,
991 /* Rectangle to paint in: */
992 QPoint(iMachineNameX, iMachineNameY),
993 /* Font to paint text: */
994 m_nameFont,
995 /* Paint device: */
996 model()->paintDevice(),
997 /* Text to paint: */
998 m_strName,
999 /* Name hovered? */
1000 m_fNameHovered ? linkTextColor : buttonTextColor);
1001}
1002
1003/* static */
1004void UIDetailsElement::paintPixmap(QPainter *pPainter, const QRect &rect, const QPixmap &pixmap)
1005{
1006 pPainter->drawPixmap(rect, pixmap);
1007}
1008
1009/* static */
1010void UIDetailsElement::paintText(QPainter *pPainter, QPoint point,
1011 const QFont &font, QPaintDevice *pPaintDevice,
1012 const QString &strText, const QColor &color)
1013{
1014 /* Prepare variables: */
1015 QFontMetrics fm(font, pPaintDevice);
1016 point += QPoint(0, fm.ascent());
1017
1018 /* Draw text: */
1019 pPainter->save();
1020 pPainter->setFont(font);
1021 pPainter->setPen(color);
1022 pPainter->drawText(point, strText);
1023 pPainter->restore();
1024}
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