VirtualBox

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

Last change on this file since 104393 was 104393, checked in by vboxsync, 7 months ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in the UIActionPool class.

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