VirtualBox

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

Last change on this file since 105266 was 105246, checked in by vboxsync, 10 months ago

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

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