VirtualBox

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

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

FE/Qt: bugref:7720: Meh, proper build fix for r132543.

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