VirtualBox

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

Last change on this file since 94530 was 94530, checked in by vboxsync, 3 years ago

FE/Qt/Ds: bugref:6899: Machine settings: Network page accessibility improvements (part 2); A bit of cleanup for attachment type editor.

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