VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.cpp@ 101559

Last change on this file since 101559 was 101559, checked in by vboxsync, 12 months ago

FE/Qt: bugref:10450: Get rid of Qt5 stuff; This one is about device-pixel-ratio stuff.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 84.3 KB
Line 
1/* $Id: UISnapshotDetailsWidget.cpp 101559 2023-10-23 15:51:00Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UISnapshotDetailsWidget class implementation.
4 */
5
6/*
7 * Copyright (C) 2008-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 <QAccessibleWidget>
30#include <QHBoxLayout>
31#include <QDateTime>
32#include <QDir>
33#include <QGridLayout>
34#include <QLabel>
35#include <QLineEdit>
36#include <QPainter>
37#include <QPushButton>
38#include <QRegularExpression>
39#include <QScrollArea>
40#include <QTabWidget>
41#include <QTextBrowser>
42#include <QTextEdit>
43#include <QVBoxLayout>
44#ifdef VBOX_IS_QT6_OR_LATER
45# include <QWindow>
46#endif
47
48/* GUI includes: */
49#include "QIDialogButtonBox.h"
50#include "QIFlowLayout.h"
51#include "UICommon.h"
52#include "UIConverter.h"
53#include "UICursor.h"
54#include "UIDesktopWidgetWatchdog.h"
55#include "UIIconPool.h"
56#include "UIGuestOSType.h"
57#include "UISnapshotDetailsWidget.h"
58#include "UIMessageCenter.h"
59#include "UITranslator.h"
60#include "VBoxUtils.h"
61
62/* COM includes: */
63#include "CAudioAdapter.h"
64#include "CAudioSettings.h"
65#include "CFirmwareSettings.h"
66#include "CRecordingSettings.h"
67#include "CRecordingScreenSettings.h"
68#include "CMachine.h"
69#include "CMedium.h"
70#include "CMediumAttachment.h"
71#include "CNetworkAdapter.h"
72#include "CPlatform.h"
73#include "CPlatformX86.h"
74#include "CPlatformProperties.h"
75#include "CSerialPort.h"
76#include "CSharedFolder.h"
77#include "CStorageController.h"
78#include "CUSBController.h"
79#include "CUSBDeviceFilter.h"
80#include "CUSBDeviceFilters.h"
81#include "CVRDEServer.h"
82
83/* Forward declarations: */
84class UISnapshotDetailsElement;
85
86
87/** QAccessibleObject extension used as an accessibility interface for UISnapshotDetailsElement. */
88class UIAccessibilityInterfaceForUISnapshotDetailsElement : public QAccessibleWidget
89{
90public:
91
92 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
93 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
94 {
95 /* Creating UISnapshotDetailsElement accessibility interface: */
96 if (pObject && strClassname == QLatin1String("UISnapshotDetailsElement"))
97 return new UIAccessibilityInterfaceForUISnapshotDetailsElement(qobject_cast<QWidget*>(pObject));
98
99 /* Null by default: */
100 return 0;
101 }
102
103 /** Constructs an accessibility interface passing @a pWidget to the base-class. */
104 UIAccessibilityInterfaceForUISnapshotDetailsElement(QWidget *pWidget)
105 : QAccessibleWidget(pWidget, QAccessible::StaticText)
106 {}
107
108 /** Returns a text for the passed @a enmTextRole. */
109 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
110
111private:
112
113 /** Returns corresponding UISnapshotDetailsElement. */
114 UISnapshotDetailsElement *browser() const;
115};
116
117
118/** QWiget extension providing GUI with snapshot details elements. */
119class UISnapshotDetailsElement : public QWidget
120{
121 Q_OBJECT;
122
123signals:
124
125 /** Notifies listeners about @a link was clicked. */
126 void sigAnchorClicked(const QUrl &link);
127
128public:
129
130 /** Constructs details element passing @a pParent to the base-class.
131 * @param strName Brings the element name.
132 * @param icon Brings the element icon.
133 * @param fLinkSupport Brings whether we should construct text-browser
134 * instead of simple text-edit otherwise. */
135 UISnapshotDetailsElement(const QString &strName, const QIcon &icon,
136 bool fLinkSupport, QWidget *pParent = 0);
137
138 /** Returns underlying text-document. */
139 QTextDocument *document() const;
140
141 /** Defines text-document text. */
142 void setText(const QString &strText);
143
144 /** Returns the minimum size-hint. */
145 QSize minimumSizeHint() const;
146
147protected:
148
149 /** Handles any Qt @a pEvent. */
150 virtual bool event(QEvent *pEvent) RT_OVERRIDE;
151
152 /** Handles paint @a pEvent. */
153 virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
154
155private:
156
157 /** Prepares all. */
158 void prepare();
159
160 /** Updates pixmap. */
161 void updatePixmap();
162
163 /** Holds the element name.*/
164 QString m_strName;
165 /** Holds the element icon. */
166 QIcon m_icon;
167 /** Holds whether we should construct text-browser
168 * instead of simple text-edit otherwise. */
169 bool m_fLinkSupport;
170
171 /** Holds the text-edit interface instance. */
172 QTextEdit *m_pTextEdit;
173};
174
175
176/** QWiget extension providing GUI with snapshot screenshot viewer widget. */
177class UIScreenshotViewer : public QIWithRetranslateUI2<QWidget>
178{
179 Q_OBJECT;
180
181public:
182
183 /** Constructs screenshow viewer passing @a pParent to the base-class.
184 * @param pixmapScreenshot Brings the screenshot to show.
185 * @param strSnapshotName Brings the snapshot name.
186 * @param strMachineName Brings the machine name. */
187 UIScreenshotViewer(const QPixmap &pixmapScreenshot,
188 const QString &strSnapshotName,
189 const QString &strMachineName,
190 QWidget *pParent = 0);
191
192protected:
193
194 /** Handles translation event. */
195 virtual void retranslateUi() RT_OVERRIDE;
196
197 /** Handles show @a pEvent. */
198 virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
199 /** Handles polish @a pEvent. */
200 virtual void polishEvent(QShowEvent *pEvent);
201
202 /** Handles resize @a pEvent. */
203 virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
204
205 /** Handles mouse press @a pEvent. */
206 virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
207 /** Handles key press @a pEvent. */
208 virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
209
210private:
211
212 /** Prepares all. */
213 void prepare();
214
215 /** Adjusts window size. */
216 void adjustWindowSize();
217
218 /** Adjusts picture. */
219 void adjustPicture();
220
221 /** Holds whether this widget was polished. */
222 bool m_fPolished;
223
224 /** Holds the screenshot to show. */
225 QPixmap m_pixmapScreenshot;
226 /** Holds the snapshot name. */
227 QString m_strSnapshotName;
228 /** Holds the machine name. */
229 QString m_strMachineName;
230
231 /** Holds the scroll-area instance. */
232 QScrollArea *m_pScrollArea;
233 /** Holds the picture label instance. */
234 QLabel *m_pLabelPicture;
235
236 /** Holds whether we are in zoom mode. */
237 bool m_fZoomMode;
238};
239
240
241/*********************************************************************************************************************************
242* Class UIAccessibilityInterfaceForUISnapshotDetailsElement implementation. *
243*********************************************************************************************************************************/
244
245QString UIAccessibilityInterfaceForUISnapshotDetailsElement::text(QAccessible::Text enmTextRole) const
246{
247 /* Make sure browser still alive: */
248 AssertPtrReturn(browser(), QString());
249
250 /* Return the description: */
251 if (enmTextRole == QAccessible::Description)
252 {
253 /* Sanity check: */
254 AssertPtrReturn(browser()->document(), QString());
255 return browser()->document()->toPlainText();
256 }
257
258 /* Null-string by default: */
259 return QString();
260}
261
262UISnapshotDetailsElement *UIAccessibilityInterfaceForUISnapshotDetailsElement::browser() const
263{
264 return qobject_cast<UISnapshotDetailsElement*>(widget());
265}
266
267
268/*********************************************************************************************************************************
269* Class UISnapshotDetailsElement implementation. *
270*********************************************************************************************************************************/
271
272UISnapshotDetailsElement::UISnapshotDetailsElement(const QString &strName, const QIcon &icon,
273 bool fLinkSupport, QWidget *pParent /* = 0 */)
274 : QWidget(pParent)
275 , m_strName(strName)
276 , m_icon(icon)
277 , m_fLinkSupport(fLinkSupport)
278 , m_pTextEdit(0)
279{
280 /* Prepare: */
281 prepare();
282}
283
284QTextDocument *UISnapshotDetailsElement::document() const
285{
286 /* Pass to private object: */
287 return m_pTextEdit->document();
288}
289
290void UISnapshotDetailsElement::setText(const QString &strText)
291{
292 /* Pass to private object: */
293 m_pTextEdit->setText(strText);
294 /* Update the layout: */
295 updateGeometry();
296}
297
298QSize UISnapshotDetailsElement::minimumSizeHint() const
299{
300 /* Calculate minimum size-hint on the basis of:
301 * 1. context and text-documnt margins, 2. text-document ideal width and height: */
302 int iTop = 0, iLeft = 0, iRight = 0, iBottom = 0;
303 layout()->getContentsMargins(&iTop, &iLeft, &iRight, &iBottom);
304 const QSize size = m_pTextEdit->document()->size().toSize();
305 const int iDocumentMargin = (int)m_pTextEdit->document()->documentMargin();
306 const int iIdealWidth = (int)m_pTextEdit->document()->idealWidth() + 2 * iDocumentMargin + iLeft + iRight;
307 const int iIdealHeight = size.height() + 2 * iDocumentMargin + iTop + iBottom;
308 return QSize(iIdealWidth, iIdealHeight);
309}
310
311bool UISnapshotDetailsElement::event(QEvent *pEvent)
312{
313 /* Handle know event types: */
314 switch (pEvent->type())
315 {
316 case QEvent::Show:
317 case QEvent::ScreenChangeInternal:
318 {
319 /* Update pixmap: */
320 updatePixmap();
321 break;
322 }
323 default:
324 break;
325 }
326
327 /* Call to base-class: */
328 return QWidget::event(pEvent);
329}
330
331void UISnapshotDetailsElement::paintEvent(QPaintEvent * /* pEvent */)
332{
333 /* Prepare painter: */
334 QPainter painter(this);
335
336 /* Prepare palette colors: */
337 const QPalette pal = QApplication::palette();
338 QColor color0 = pal.color(QPalette::Window);
339 QColor color1 = pal.color(QPalette::Window).lighter(110);
340 color1.setAlpha(0);
341 QColor color2 = pal.color(QPalette::Window).darker(200);
342
343 /* Invent pixel metric: */
344 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
345
346 /* Top-left corner: */
347 QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
348 {
349 grad1.setColorAt(0, color2);
350 grad1.setColorAt(1, color1);
351 }
352 /* Top-right corner: */
353 QRadialGradient grad2(QPointF(width() - iMetric, iMetric), iMetric);
354 {
355 grad2.setColorAt(0, color2);
356 grad2.setColorAt(1, color1);
357 }
358 /* Bottom-left corner: */
359 QRadialGradient grad3(QPointF(iMetric, height() - iMetric), iMetric);
360 {
361 grad3.setColorAt(0, color2);
362 grad3.setColorAt(1, color1);
363 }
364 /* Botom-right corner: */
365 QRadialGradient grad4(QPointF(width() - iMetric, height() - iMetric), iMetric);
366 {
367 grad4.setColorAt(0, color2);
368 grad4.setColorAt(1, color1);
369 }
370
371 /* Top line: */
372 QLinearGradient grad5(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
373 {
374 grad5.setColorAt(0, color1);
375 grad5.setColorAt(1, color2);
376 }
377 /* Bottom line: */
378 QLinearGradient grad6(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
379 {
380 grad6.setColorAt(0, color1);
381 grad6.setColorAt(1, color2);
382 }
383 /* Left line: */
384 QLinearGradient grad7(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
385 {
386 grad7.setColorAt(0, color1);
387 grad7.setColorAt(1, color2);
388 }
389 /* Right line: */
390 QLinearGradient grad8(QPointF(width(), height() - iMetric), QPointF(width() - iMetric, height() - iMetric));
391 {
392 grad8.setColorAt(0, color1);
393 grad8.setColorAt(1, color2);
394 }
395
396 /* Paint shape/shadow: */
397 painter.fillRect(QRect(iMetric, iMetric, width() - iMetric * 2, height() - iMetric * 2), color0);
398 painter.fillRect(QRect(0, 0, iMetric, iMetric), grad1);
399 painter.fillRect(QRect(width() - iMetric, 0, iMetric, iMetric), grad2);
400 painter.fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad3);
401 painter.fillRect(QRect(width() - iMetric, height() - iMetric, iMetric, iMetric), grad4);
402 painter.fillRect(QRect(iMetric, 0, width() - iMetric * 2, iMetric), grad5);
403 painter.fillRect(QRect(iMetric, height() - iMetric, width() - iMetric * 2, iMetric), grad6);
404 painter.fillRect(QRect(0, iMetric, iMetric, height() - iMetric * 2), grad7);
405 painter.fillRect(QRect(width() - iMetric, iMetric, iMetric, height() - iMetric * 2), grad8);
406}
407
408void UISnapshotDetailsElement::prepare()
409{
410 /* Install QIComboBox accessibility interface factory: */
411 QAccessible::installFactory(UIAccessibilityInterfaceForUISnapshotDetailsElement::pFactory);
412
413 /* Create layout: */
414 new QHBoxLayout(this);
415 AssertPtrReturnVoid(layout());
416 {
417 /* Invent pixel metric: */
418 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
419
420 /* Configure layout: */
421 layout()->setContentsMargins(iMetric, iMetric, iMetric, iMetric);
422
423 /* Create text-browser if requested, text-edit otherwise: */
424 m_pTextEdit = m_fLinkSupport ? new QTextBrowser : new QTextEdit;
425 AssertPtrReturnVoid(m_pTextEdit);
426 {
427 /* Configure that we have: */
428 m_pTextEdit->setReadOnly(true);
429 m_pTextEdit->setFocusPolicy(Qt::NoFocus);
430 m_pTextEdit->setFrameShape(QFrame::NoFrame);
431 m_pTextEdit->viewport()->setAutoFillBackground(false);
432 m_pTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
433 m_pTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
434 m_pTextEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
435 if (m_fLinkSupport)
436 {
437 // WORKAROUND:
438 // Intentionally using old kind of API here:
439 connect(m_pTextEdit, SIGNAL(anchorClicked(const QUrl &)),
440 this, SIGNAL(sigAnchorClicked(const QUrl &)));
441 }
442
443 /* Add into layout: */
444 layout()->addWidget(m_pTextEdit);
445 }
446 }
447
448 /* Update pixmap: */
449 updatePixmap();
450}
451
452void UISnapshotDetailsElement::updatePixmap()
453{
454 /* Re-register icon in the element's text-document: */
455 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
456 const qreal fDevicePixelRatio = window() && window()->windowHandle() ? window()->windowHandle()->devicePixelRatio() : 1;
457 document()->addResource(
458 QTextDocument::ImageResource,
459 QUrl(QString("details://%1").arg(m_strName)),
460 QVariant(m_icon.pixmap(QSize(iMetric, iMetric), fDevicePixelRatio)));
461}
462
463
464/*********************************************************************************************************************************
465* Class UIScreenshotViewer implementation. *
466*********************************************************************************************************************************/
467
468UIScreenshotViewer::UIScreenshotViewer(const QPixmap &pixmapScreenshot,
469 const QString &strSnapshotName,
470 const QString &strMachineName,
471 QWidget *pParent /* = 0 */)
472 : QIWithRetranslateUI2<QWidget>(pParent, Qt::Tool)
473 , m_fPolished(false)
474 , m_pixmapScreenshot(pixmapScreenshot)
475 , m_strSnapshotName(strSnapshotName)
476 , m_strMachineName(strMachineName)
477 , m_pScrollArea(0)
478 , m_pLabelPicture(0)
479 , m_fZoomMode(true)
480{
481 /* Prepare: */
482 prepare();
483}
484
485void UIScreenshotViewer::retranslateUi()
486{
487 /* Translate window title: */
488 setWindowTitle(tr("Screenshot of %1 (%2)").arg(m_strSnapshotName).arg(m_strMachineName));
489}
490
491void UIScreenshotViewer::showEvent(QShowEvent *pEvent)
492{
493 /* Call to base-class: */
494 QIWithRetranslateUI2<QWidget>::showEvent(pEvent);
495
496 /* Make sure we should polish dialog: */
497 if (m_fPolished)
498 return;
499
500 /* Call to polish-event: */
501 polishEvent(pEvent);
502
503 /* Mark dialog as polished: */
504 m_fPolished = true;
505}
506
507void UIScreenshotViewer::polishEvent(QShowEvent * /* pEvent */)
508{
509 /* Adjust the picture: */
510 adjustPicture();
511}
512
513void UIScreenshotViewer::resizeEvent(QResizeEvent *pEvent)
514{
515 /* Call to base-class: */
516 QIWithRetranslateUI2<QWidget>::resizeEvent(pEvent);
517
518 /* Adjust the picture: */
519 adjustPicture();
520}
521
522void UIScreenshotViewer::mousePressEvent(QMouseEvent *pEvent)
523{
524 /* Toggle the zoom mode: */
525 m_fZoomMode = !m_fZoomMode;
526
527 /* Adjust the windiow size: */
528 adjustWindowSize();
529 /* Adjust the picture: */
530 adjustPicture();
531
532 /* Call to base-class: */
533 QIWithRetranslateUI2<QWidget>::mousePressEvent(pEvent);
534}
535
536void UIScreenshotViewer::keyPressEvent(QKeyEvent *pEvent)
537{
538 /* Close on escape: */
539 if (pEvent->key() == Qt::Key_Escape)
540 close();
541
542 /* Call to base-class: */
543 QIWithRetranslateUI2<QWidget>::keyPressEvent(pEvent);
544}
545
546void UIScreenshotViewer::prepare()
547{
548 /* Screenshot viewer is an application-modal window: */
549 setWindowModality(Qt::ApplicationModal);
550 /* With the pointing-hand cursor: */
551 UICursor::setCursor(this, Qt::PointingHandCursor);
552 /* And it's being deleted when closed: */
553 setAttribute(Qt::WA_DeleteOnClose);
554
555 /* Create layout: */
556 new QVBoxLayout(this);
557 AssertPtrReturnVoid(layout());
558 {
559 /* Configure layout: */
560 layout()->setContentsMargins(0, 0, 0, 0);
561
562 /* Create scroll-area: */
563 m_pScrollArea = new QScrollArea;
564 AssertPtrReturnVoid(m_pScrollArea);
565 {
566 /* Configure scroll-area: */
567 m_pScrollArea->setWidgetResizable (true);
568
569 /* Create picture label: */
570 m_pLabelPicture = new QLabel;
571 AssertPtrReturnVoid(m_pLabelPicture);
572 {
573 /* Add into scroll-area: */
574 m_pScrollArea->setWidget(m_pLabelPicture);
575 }
576
577 /* Add into layout: */
578 layout()->addWidget(m_pScrollArea);
579 }
580 }
581
582 /* Apply language settings: */
583 retranslateUi();
584
585 /* Adjust window size: */
586 adjustWindowSize();
587
588 /* Center according requested widget: */
589 gpDesktop->centerWidget(this, parentWidget(), false);
590}
591
592void UIScreenshotViewer::adjustWindowSize()
593{
594 /* Acquire current host-screen size, fallback to 1024x768 if failed: */
595 QSize screenSize = gpDesktop->screenGeometry(parentWidget()).size();
596 if (!screenSize.isValid())
597 screenSize = QSize(1024, 768);
598 const int iInitWidth = screenSize.width() * .50 /* 50% of host-screen width */;
599
600 /* Calculate screenshot aspect-ratio: */
601 const double dAspectRatio = (double)m_pixmapScreenshot.height() / m_pixmapScreenshot.width();
602
603 /* Calculate maximum window size: */
604 const QSize maxSize = m_fZoomMode
605 ? screenSize * .9 /* 90% of host-screen size */ +
606 QSize(m_pScrollArea->frameWidth() * 2, m_pScrollArea->frameWidth() * 2)
607 : m_pixmapScreenshot.size() /* just the screenshot size */ +
608 QSize(m_pScrollArea->frameWidth() * 2, m_pScrollArea->frameWidth() * 2);
609
610 /* Calculate initial window size: */
611 const QSize initSize = QSize(iInitWidth, (int)(iInitWidth * dAspectRatio)).boundedTo(maxSize);
612
613 /* Apply maximum window size restrictions: */
614 setMaximumSize(maxSize);
615 /* Apply initial window size: */
616 resize(initSize);
617}
618
619void UIScreenshotViewer::adjustPicture()
620{
621 if (m_fZoomMode)
622 {
623 /* Adjust visual aspects: */
624 m_pScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
625 m_pScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
626 m_pLabelPicture->setPixmap(m_pixmapScreenshot.scaled(m_pScrollArea->viewport()->size(),
627 Qt::IgnoreAspectRatio,
628 Qt::SmoothTransformation));
629 m_pLabelPicture->setToolTip(tr("Click to view non-scaled screenshot."));
630 }
631 else
632 {
633 /* Adjust visual aspects: */
634 m_pScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
635 m_pScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
636 m_pLabelPicture->setPixmap(m_pixmapScreenshot);
637 m_pLabelPicture->setToolTip(tr("Click to view scaled screenshot."));
638 }
639}
640
641
642/*********************************************************************************************************************************
643* Class UISnapshotDetailsWidget implementation. *
644*********************************************************************************************************************************/
645
646UISnapshotDetailsWidget::UISnapshotDetailsWidget(QWidget *pParent /* = 0 */)
647 : QIWithRetranslateUI<QWidget>(pParent)
648 , m_pTabWidget(0)
649 , m_pLayoutOptions(0)
650 , m_pLabelName(0), m_pEditorName(0), m_pErrorPaneName(0)
651 , m_pLabelDescription(0), m_pBrowserDescription(0), m_pErrorPaneDescription(0)
652 , m_pButtonBox(0)
653 , m_pLayoutDetails(0)
654 , m_pScrollAreaDetails(0)
655{
656 /* Prepare: */
657 prepare();
658}
659
660void UISnapshotDetailsWidget::setData(const CMachine &comMachine)
661{
662 /* Cache old/new data: */
663 m_oldData = UIDataSnapshot();
664 m_newData = m_oldData;
665
666 /* Cache machine/snapshot: */
667 m_comMachine = comMachine;
668 m_comSnapshot = CSnapshot();
669
670 /* Retranslate buttons: */
671 retranslateButtons();
672 /* Load snapshot data: */
673 loadSnapshotData();
674}
675
676void UISnapshotDetailsWidget::setData(const UIDataSnapshot &data, const CSnapshot &comSnapshot)
677{
678 /* Cache old/new data: */
679 m_oldData = data;
680 m_newData = m_oldData;
681
682 /* Cache machine/snapshot: */
683 m_comMachine = CMachine();
684 m_comSnapshot = comSnapshot;
685
686 /* Retranslate buttons: */
687 retranslateButtons();
688 /* Load snapshot data: */
689 loadSnapshotData();
690}
691
692void UISnapshotDetailsWidget::clearData()
693{
694 /* Reset old/new data: */
695 m_oldData = UIDataSnapshot();
696 m_newData = m_oldData;
697
698 /* Reset machine/snapshot: */
699 m_comMachine = CMachine();
700 m_comSnapshot = CSnapshot();
701
702 /* Retranslate buttons: */
703 retranslateButtons();
704 /* Load snapshot data: */
705 loadSnapshotData();
706}
707
708void UISnapshotDetailsWidget::retranslateUi()
709{
710 /* Translate labels: */
711 m_pTabWidget->setTabText(0, tr("&Attributes"));
712 m_pTabWidget->setTabText(1, tr("&Information"));
713 m_pLabelName->setText(tr("&Name:"));
714 m_pLabelDescription->setText(tr("&Description:"));
715 m_pEditorName->setToolTip(tr("Holds the snapshot name."));
716 m_pBrowserDescription->setToolTip(tr("Holds the snapshot description."));
717
718 /* Translate placeholders: */
719 m_pEditorName->setPlaceholderText( m_comMachine.isNotNull()
720 ? tr("Enter a name for the new snapshot...")
721 : m_comSnapshot.isNotNull()
722 ? tr("Enter a name for this snapshot...")
723 : QString());
724
725 /* Translate buttons: */
726 m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
727 m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
728 retranslateButtons();
729
730 /* Update the picture tool-tip and visibility: */
731 m_details.value(DetailsElementType_Preview)->setToolTip(tr("Click to enlarge the screenshot."));
732 if (!m_pixmapScreenshot.isNull() && m_details.value(DetailsElementType_Preview)->isHidden())
733 m_details.value(DetailsElementType_Preview)->setHidden(false);
734 else if (m_pixmapScreenshot.isNull() && !m_details.value(DetailsElementType_Preview)->isHidden())
735 m_details.value(DetailsElementType_Preview)->setHidden(true);
736
737 /* Prepare machine: */
738 const CMachine &comMachine = m_comMachine.isNotNull()
739 ? m_comMachine
740 : m_comSnapshot.isNotNull()
741 ? m_comSnapshot.GetMachine()
742 : CMachine();
743
744 /* Make sure machine is valid: */
745 if (comMachine.isNotNull())
746 {
747 /* Update USB details visibility: */
748 const CUSBDeviceFilters &comFilters = comMachine.GetUSBDeviceFilters();
749 const bool fUSBMissing = comFilters.isNull() || !comMachine.GetUSBProxyAvailable();
750 if (fUSBMissing && !m_details.value(DetailsElementType_USB)->isHidden())
751 m_details.value(DetailsElementType_USB)->setHidden(true);
752
753 /* Rebuild the details report: */
754 foreach (const DetailsElementType &enmType, m_details.keys())
755 m_details.value(enmType)->setText(detailsReport(enmType, comMachine, comMachine.GetCurrentSnapshot()));
756 }
757
758 /* Retranslate validation: */
759 retranslateValidation();
760}
761
762void UISnapshotDetailsWidget::retranslateButtons()
763{
764 /* Common: 'Reset' button: */
765 m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(tr("Reset"));
766 m_pButtonBox->button(QDialogButtonBox::Cancel)->setStatusTip(tr("Reset changes in current snapshot details"));
767 m_pButtonBox->button(QDialogButtonBox::Cancel)->
768 setToolTip(tr("Reset Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Cancel)->shortcut().toString()));
769
770 if (m_comMachine.isNotNull())
771 {
772 /* Machine: 'Take' button: */
773 m_pButtonBox->button(QDialogButtonBox::Ok)->setText(tr("Take"));
774 m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(tr("Take snapshot on the basis of current machine state"));
775 m_pButtonBox->button(QDialogButtonBox::Ok)->
776 setToolTip(tr("Take Snapshot (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
777 }
778 else
779 {
780 /* Snapshot: 'Apply' button: */
781 m_pButtonBox->button(QDialogButtonBox::Ok)->setText(tr("Apply"));
782 m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(tr("Apply changes in current snapshot details"));
783 m_pButtonBox->button(QDialogButtonBox::Ok)->
784 setToolTip(tr("Apply Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
785 }
786}
787
788void UISnapshotDetailsWidget::sltHandleNameChange()
789{
790 m_newData.setName(m_pEditorName->text());
791 revalidate(m_pErrorPaneName);
792 updateButtonStates();
793}
794
795void UISnapshotDetailsWidget::sltHandleDescriptionChange()
796{
797 m_newData.setDescription(m_pBrowserDescription->toPlainText());
798 revalidate(m_pErrorPaneDescription);
799 updateButtonStates();
800}
801
802void UISnapshotDetailsWidget::sltHandleAnchorClicked(const QUrl &link)
803{
804 /* Get the link out of url: */
805 const QString strLink = link.toString();
806 if (strLink == "#thumbnail")
807 {
808 /* We are creating screenshot viewer and show it: */
809 UIScreenshotViewer *pViewer = new UIScreenshotViewer(m_pixmapScreenshot,
810 m_comSnapshot.GetMachine().GetName(),
811 m_comSnapshot.GetName(),
812 this);
813 pViewer->show();
814 pViewer->activateWindow();
815 }
816}
817
818void UISnapshotDetailsWidget::sltHandleChangeAccepted()
819{
820 /* Disable buttons first of all: */
821 m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
822 m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
823
824 /* Notify listeners: */
825 emit sigDataChangeAccepted();
826}
827
828void UISnapshotDetailsWidget::sltHandleChangeRejected()
829{
830 /* Reset new data to old: */
831 m_newData = m_oldData;
832
833 /* Load snapshot data: */
834 loadSnapshotData();
835}
836
837void UISnapshotDetailsWidget::prepare()
838{
839 /* Create layout: */
840 QVBoxLayout *pLayout = new QVBoxLayout(this);
841 AssertPtrReturnVoid(pLayout);
842 {
843 /* Configure layout: */
844 pLayout->setContentsMargins(0, 0, 0, 0);
845
846 /* Create tab-widget: */
847 m_pTabWidget = new QTabWidget;
848 AssertPtrReturnVoid(m_pTabWidget);
849 {
850 /* Prepare 'Options' tab: */
851 prepareTabOptions();
852 /* Prepare 'Details' tab: */
853 prepareTabDetails();
854
855 /* Add into layout: */
856 pLayout->addWidget(m_pTabWidget);
857 }
858 }
859}
860
861void UISnapshotDetailsWidget::prepareTabOptions()
862{
863 /* Create widget itself: */
864 QWidget *pWidget = new QWidget;
865 AssertPtrReturnVoid(pWidget);
866 {
867 /* Create 'Options' layout: */
868 m_pLayoutOptions = new QGridLayout(pWidget);
869 AssertPtrReturnVoid(m_pLayoutOptions);
870 {
871#ifdef VBOX_WS_MAC
872 /* Configure layout: */
873 m_pLayoutOptions->setSpacing(10);
874 m_pLayoutOptions->setContentsMargins(10, 10, 10, 10);
875#endif
876
877 /* Get the required icon metric: */
878 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
879
880 /* Create name label: */
881 m_pLabelName = new QLabel;
882 AssertPtrReturnVoid(m_pLabelName);
883 {
884 /* Configure label: */
885 m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
886
887 /* Add into layout: */
888 m_pLayoutOptions->addWidget(m_pLabelName, 0, 0);
889 }
890 /* Create name layout: */
891 QHBoxLayout *pLayoutName = new QHBoxLayout;
892 AssertPtrReturnVoid(pLayoutName);
893 {
894 /* Create name editor: */
895 m_pEditorName = new QLineEdit;
896 AssertPtrReturnVoid(m_pEditorName);
897 {
898 /* Configure editor: */
899 m_pLabelName->setBuddy(m_pEditorName);
900 QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Minimum);
901 policy.setHorizontalStretch(1);
902 m_pEditorName->setSizePolicy(policy);
903 connect(m_pEditorName, &QLineEdit::textChanged,
904 this, &UISnapshotDetailsWidget::sltHandleNameChange);
905
906 /* Add into layout: */
907 pLayoutName->addWidget(m_pEditorName);
908 }
909 /* Create name error pane: */
910 m_pErrorPaneName = new QLabel;
911 AssertPtrReturnVoid(m_pErrorPaneName);
912 {
913 /* Configure error pane: */
914 m_pErrorPaneName->setAlignment(Qt::AlignCenter);
915 m_pErrorPaneName->setPixmap(UIIconPool::iconSet(":/status_error_16px.png")
916 .pixmap(QSize(iIconMetric, iIconMetric)));
917
918 /* Add into layout: */
919 pLayoutName->addWidget(m_pErrorPaneName);
920 }
921
922 /* Add into layout: */
923 m_pLayoutOptions->addLayout(pLayoutName, 0, 1);
924 }
925
926 /* Create description label: */
927 m_pLabelDescription = new QLabel;
928 AssertPtrReturnVoid(m_pLabelDescription);
929 {
930 /* Configure label: */
931 m_pLabelDescription->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignTop);
932
933 /* Add into layout: */
934 m_pLayoutOptions->addWidget(m_pLabelDescription, 1, 0);
935 }
936 /* Create description layout: */
937 QHBoxLayout *pLayoutDescription = new QHBoxLayout;
938 AssertPtrReturnVoid(pLayoutDescription);
939 {
940 /* Create description browser: */
941 m_pBrowserDescription = new QTextEdit;
942 AssertPtrReturnVoid(m_pBrowserDescription);
943 {
944 /* Configure browser: */
945 m_pLabelDescription->setBuddy(m_pBrowserDescription);
946 m_pBrowserDescription->setTabChangesFocus(true);
947 m_pBrowserDescription->setAcceptRichText(false);
948 QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Expanding);
949 policy.setHorizontalStretch(1);
950 m_pBrowserDescription->setSizePolicy(policy);
951 connect(m_pBrowserDescription, &QTextEdit::textChanged,
952 this, &UISnapshotDetailsWidget::sltHandleDescriptionChange);
953
954 /* Add into layout: */
955 pLayoutDescription->addWidget(m_pBrowserDescription);
956 }
957 /* Create description error pane: */
958 m_pErrorPaneDescription = new QLabel;
959 AssertPtrReturnVoid(m_pErrorPaneDescription);
960 {
961 /* Configure error pane: */
962 m_pErrorPaneDescription->setAlignment(Qt::AlignCenter);
963 m_pErrorPaneDescription->setPixmap(UIIconPool::iconSet(":/status_error_16px.png")
964 .pixmap(QSize(iIconMetric, iIconMetric)));
965
966 /* Add into layout: */
967 pLayoutDescription->addWidget(m_pErrorPaneDescription);
968 }
969
970 /* Add into layout: */
971 m_pLayoutOptions->addLayout(pLayoutDescription, 1, 1);
972 }
973
974 /* Create button-box: */
975 m_pButtonBox = new QIDialogButtonBox;
976 AssertPtrReturnVoid(m_pButtonBox);
977 {
978 /* Configure button-box: */
979 m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
980 connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UISnapshotDetailsWidget::sltHandleChangeAccepted);
981 connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UISnapshotDetailsWidget::sltHandleChangeRejected);
982
983 /* Add into layout: */
984 m_pLayoutOptions->addWidget(m_pButtonBox, 2, 0, 1, 2);
985 }
986 }
987
988 /* Add to tab-widget: */
989 m_pTabWidget->addTab(pWidget, QString());
990 }
991}
992
993void UISnapshotDetailsWidget::prepareTabDetails()
994{
995 /* Create details scroll-area: */
996 m_pScrollAreaDetails = new QScrollArea;
997 AssertPtrReturnVoid(m_pScrollAreaDetails);
998 {
999 /* Configure browser: */
1000 m_pScrollAreaDetails->setWidgetResizable(true);
1001 m_pScrollAreaDetails->setFrameShadow(QFrame::Plain);
1002 m_pScrollAreaDetails->setFrameShape(QFrame::NoFrame);
1003 m_pScrollAreaDetails->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
1004 m_pScrollAreaDetails->viewport()->setAutoFillBackground(false);
1005
1006 /* Create details widget: */
1007 QWidget *pWidgetDetails = new QWidget;
1008 AssertPtrReturnVoid(pWidgetDetails);
1009 {
1010 /* Create 'Details' layout: */
1011 m_pLayoutDetails = new QVBoxLayout(pWidgetDetails);
1012 AssertPtrReturnVoid(m_pLayoutDetails);
1013 {
1014 /* Metric: */
1015 const int iSpacing = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
1016
1017 /* Configure layout: */
1018 m_pLayoutDetails->setSpacing(iSpacing);
1019#ifdef VBOX_WS_MAC
1020 m_pLayoutDetails->setContentsMargins(10, 10, 10, 10);
1021#endif
1022
1023 /* Create layout 1: */
1024 QHBoxLayout *pLayout1 = new QHBoxLayout;
1025 AssertPtrReturnVoid(pLayout1);
1026 {
1027 /* Create left layout: */
1028 QIFlowLayout *pLayoutLeft = new QIFlowLayout;
1029 AssertPtrReturnVoid(pLayoutLeft);
1030 {
1031 /* Configure layout: */
1032 pLayoutLeft->setSpacing(iSpacing);
1033 pLayoutLeft->setContentsMargins(0, 0, 0, 0);
1034
1035 /* Create 'General' element: */
1036 m_details[DetailsElementType_General] = createDetailsElement(DetailsElementType_General);
1037 AssertPtrReturnVoid(m_details[DetailsElementType_General]);
1038 pLayoutLeft->addWidget(m_details[DetailsElementType_General]);
1039
1040 /* Create 'System' element: */
1041 m_details[DetailsElementType_System] = createDetailsElement(DetailsElementType_System);
1042 AssertPtrReturnVoid(m_details[DetailsElementType_System]);
1043 pLayoutLeft->addWidget(m_details[DetailsElementType_System]);
1044
1045 /* Add to layout: */
1046 pLayout1->addLayout(pLayoutLeft);
1047 }
1048
1049 /* Create right layout: */
1050 QVBoxLayout *pLayoutRight = new QVBoxLayout;
1051 AssertPtrReturnVoid(pLayoutRight);
1052 {
1053 /* Configure layout: */
1054 pLayoutLeft->setSpacing(iSpacing);
1055 pLayoutRight->setContentsMargins(0, 0, 0, 0);
1056
1057 /* Create 'Preview' element: */
1058 m_details[DetailsElementType_Preview] = createDetailsElement(DetailsElementType_Preview);
1059 AssertPtrReturnVoid(m_details[DetailsElementType_Preview]);
1060 connect(m_details[DetailsElementType_Preview], &UISnapshotDetailsElement::sigAnchorClicked,
1061 this, &UISnapshotDetailsWidget::sltHandleAnchorClicked);
1062 pLayoutRight->addWidget(m_details[DetailsElementType_Preview]);
1063 pLayoutRight->addStretch();
1064
1065 /* Add to layout: */
1066 pLayout1->addLayout(pLayoutRight);
1067 }
1068
1069 /* Add into layout: */
1070 m_pLayoutDetails->addLayout(pLayout1);
1071 }
1072
1073 /* Create layout 2: */
1074 QIFlowLayout *pLayout2 = new QIFlowLayout;
1075 {
1076 /* Configure layout: */
1077 pLayout2->setSpacing(iSpacing);
1078
1079 /* Create 'Display' element: */
1080 m_details[DetailsElementType_Display] = createDetailsElement(DetailsElementType_Display);
1081 AssertPtrReturnVoid(m_details[DetailsElementType_Display]);
1082 pLayout2->addWidget(m_details[DetailsElementType_Display]);
1083
1084 /* Create 'Audio' element: */
1085 m_details[DetailsElementType_Audio] = createDetailsElement(DetailsElementType_Audio);
1086 AssertPtrReturnVoid(m_details[DetailsElementType_Audio]);
1087 pLayout2->addWidget(m_details[DetailsElementType_Audio]);
1088
1089 /* Create 'Storage' element: */
1090 m_details[DetailsElementType_Storage] = createDetailsElement(DetailsElementType_Storage);
1091 AssertPtrReturnVoid(m_details[DetailsElementType_Storage]);
1092 pLayout2->addWidget(m_details[DetailsElementType_Storage]);
1093
1094 /* Create 'Network' element: */
1095 m_details[DetailsElementType_Network] = createDetailsElement(DetailsElementType_Network);
1096 AssertPtrReturnVoid(m_details[DetailsElementType_Network]);
1097 pLayout2->addWidget(m_details[DetailsElementType_Network]);
1098
1099 /* Create 'Serial' element: */
1100 m_details[DetailsElementType_Serial] = createDetailsElement(DetailsElementType_Serial);
1101 AssertPtrReturnVoid(m_details[DetailsElementType_Serial]);
1102 pLayout2->addWidget(m_details[DetailsElementType_Serial]);
1103
1104 /* Create 'USB' element: */
1105 m_details[DetailsElementType_USB] = createDetailsElement(DetailsElementType_USB);
1106 AssertPtrReturnVoid(m_details[DetailsElementType_USB]);
1107 pLayout2->addWidget(m_details[DetailsElementType_USB]);
1108
1109 /* Create 'SF' element: */
1110 m_details[DetailsElementType_SF] = createDetailsElement(DetailsElementType_SF);
1111 AssertPtrReturnVoid(m_details[DetailsElementType_SF]);
1112 pLayout2->addWidget(m_details[DetailsElementType_SF]);
1113
1114 /* Add into layout: */
1115 m_pLayoutDetails->addLayout(pLayout2);
1116 }
1117
1118 /* Add stretch: */
1119 m_pLayoutDetails->addStretch();
1120 }
1121
1122 /* Add to scroll-area: */
1123 m_pScrollAreaDetails->setWidget(pWidgetDetails);
1124 pWidgetDetails->setAutoFillBackground(false);
1125 }
1126
1127 /* Add to tab-widget: */
1128 m_pTabWidget->addTab(m_pScrollAreaDetails, QString());
1129 }
1130}
1131
1132/* static */
1133UISnapshotDetailsElement *UISnapshotDetailsWidget::createDetailsElement(DetailsElementType enmType)
1134{
1135 /* Create element: */
1136 const bool fWithHypertextNavigation = enmType == DetailsElementType_Preview;
1137 UISnapshotDetailsElement *pElement = new UISnapshotDetailsElement(gpConverter->toInternalString(enmType),
1138 gpConverter->toIcon(enmType),
1139 fWithHypertextNavigation);
1140 AssertPtrReturn(pElement, 0);
1141 {
1142 /* Configure element: */
1143 switch (enmType)
1144 {
1145 case DetailsElementType_Preview:
1146 pElement->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
1147 break;
1148 default:
1149 pElement->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
1150 break;
1151 }
1152 }
1153 /* Return element: */
1154 return pElement;
1155}
1156
1157void UISnapshotDetailsWidget::loadSnapshotData()
1158{
1159 /* Read general snapshot properties: */
1160 m_pEditorName->setText(m_newData.name());
1161 m_pBrowserDescription->setText(m_newData.description());
1162 revalidate();
1163
1164 /* If there is a machine: */
1165 if (m_comMachine.isNotNull())
1166 {
1167 /* No screenshot: */
1168 m_pixmapScreenshot = QPixmap();
1169 }
1170 /* If there is a snapshot: */
1171 else if (m_comSnapshot.isNotNull())
1172 {
1173 /* Read snapshot display contents: */
1174 CMachine comMachine = m_comSnapshot.GetMachine();
1175 ULONG iWidth = 0, iHeight = 0;
1176
1177 /* Get screenshot if present: */
1178 QVector<BYTE> screenData = comMachine.ReadSavedScreenshotToArray(0, KBitmapFormat_PNG, iWidth, iHeight);
1179 m_pixmapScreenshot = screenData.size() != 0 ? QPixmap::fromImage(QImage::fromData(screenData.data(),
1180 screenData.size(),
1181 "PNG"))
1182 : QPixmap();
1183
1184 /* Register thumbnail pixmap in preview element: */
1185 // WORKAROUND:
1186 // We are generating it from the screenshot because thumbnail
1187 // returned by the CMachine::ReadSavedThumbnailToArray is too small.
1188 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
1189 const QSize thumbnailSize = QSize(iIconMetric * 4, iIconMetric * 4);
1190 const QPixmap pixThumbnail = m_pixmapScreenshot.isNull() ? m_pixmapScreenshot
1191 : m_pixmapScreenshot.scaled(thumbnailSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1192 m_details.value(DetailsElementType_Preview)->document()->addResource(
1193 QTextDocument::ImageResource, QUrl("details://thumbnail"), QVariant(pixThumbnail));
1194 }
1195
1196 /* Retranslate: */
1197 retranslateUi();
1198
1199 /* Update button states finally: */
1200 updateButtonStates();
1201}
1202
1203void UISnapshotDetailsWidget::revalidate(QWidget *pWidget /* = 0 */)
1204{
1205 if (!pWidget || pWidget == m_pErrorPaneName)
1206 {
1207 const bool fError = m_newData.name().isEmpty();
1208 m_pErrorPaneName->setVisible(fError && m_comMachine.isNull());
1209 }
1210 if (!pWidget || pWidget == m_pErrorPaneDescription)
1211 {
1212 const bool fError = false;
1213 m_pErrorPaneDescription->setVisible(fError);
1214 }
1215
1216 /* Retranslate validation: */
1217 retranslateValidation(pWidget);
1218}
1219
1220void UISnapshotDetailsWidget::retranslateValidation(QWidget *pWidget /* = 0 */)
1221{
1222 if (!pWidget || pWidget == m_pErrorPaneName)
1223 m_pErrorPaneName->setToolTip(tr("Snapshot name is empty"));
1224}
1225
1226void UISnapshotDetailsWidget::updateButtonStates()
1227{
1228// if (m_oldData != m_newData)
1229// printf("Snapshot: %s, %s\n",
1230// m_newData.m_strName.toUtf8().constData(),
1231// m_newData.m_strDescription.toUtf8().constData());
1232
1233 /* Update 'Apply' / 'Reset' button states: */
1234 m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
1235 m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
1236}
1237
1238QString UISnapshotDetailsWidget::detailsReport(DetailsElementType enmType,
1239 const CMachine &comMachine,
1240 const CSnapshot &comSnapshot /* = CSnapshot() */) const
1241{
1242 /* Details templates: */
1243 static const char *sTableTpl =
1244 "<table border=0 cellspacing=1 cellpadding=0 style='white-space:pre'>%1</table>";
1245 static const char *sSectionBoldTpl1 =
1246 "<tr>"
1247 "<td width=%3 rowspan=%1 align=left><img src='%2'></td>"
1248 "<td colspan=3><nobr><b>%4</b></nobr></td>"
1249 "</tr>"
1250 "%5";
1251 static const char *sSectionBoldTpl2 =
1252 "<tr>"
1253 "<td width=%3 rowspan=%1 align=left><img src='%2'></td>"
1254 "<td><nobr><b>%4</b></nobr></td>"
1255 "</tr>"
1256 "%5";
1257 static const char *sSectionItemTpl1 =
1258 "<tr><td><nobr>%1</nobr></td><td/><td/></tr>";
1259 static const char *sSectionItemTpl2 =
1260 "<tr><td><nobr>%1:</nobr></td><td/><td>%2</td></tr>";
1261 static const char *sSectionItemTpl3 =
1262 "<tr><td><nobr>%1</nobr></td><td/><td/></tr>";
1263 static const char *sSectionItemTpl4 =
1264 "<tr><td><a href='%2'><img src='%1'/></a></td></tr>";
1265
1266 /* Use the const ref on the basis of implicit QString constructor: */
1267 const QString &strSectionTpl = enmType == DetailsElementType_Preview
1268 ? sSectionBoldTpl2 : sSectionBoldTpl1;
1269
1270 /* Determine icon metric: */
1271 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
1272 const int iIconArea = iIconMetric * 1.375;
1273
1274 /* Acquire current snapshot machine if any: */
1275 const CMachine comMachineOld = comSnapshot.isNotNull() ? comSnapshot.GetMachine() : comMachine;
1276
1277 /* Compose report: */
1278 QString strReport;
1279 QString strItem;
1280 int iRowCount = 0;
1281 switch (enmType)
1282 {
1283 case DetailsElementType_General:
1284 {
1285 /* Name: */
1286 ++iRowCount;
1287 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Name", "details (general)"),
1288 empReport(comMachine.GetName(), comMachineOld.GetName()));
1289
1290 /* Operating System: */
1291 ++iRowCount;
1292 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Operating System", "details (general)"),
1293 empReport(uiCommon().guestOSTypeManager().getDescription(comMachine.GetOSTypeId()),
1294 uiCommon().guestOSTypeManager().getDescription(comMachineOld.GetOSTypeId())));
1295
1296 /* Location of the settings file: */
1297 QString strSettingsFilePath = comMachine.GetSettingsFilePath();
1298 QString strOldSettingsFilePath = comMachineOld.GetSettingsFilePath();
1299 QString strSettingsFolder = !strSettingsFilePath.isEmpty() ?
1300 QDir::toNativeSeparators(QFileInfo(strSettingsFilePath).absolutePath()) : QString();
1301 QString strOldSettingsFolder = !strOldSettingsFilePath.isEmpty() ?
1302 QDir::toNativeSeparators(QFileInfo(strOldSettingsFilePath).absolutePath()) : QString();
1303
1304 ++iRowCount;
1305 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Settings File Location", "details (general)"),
1306 empReport(strSettingsFolder, strOldSettingsFolder));
1307
1308 /* Groups? */
1309 const QString strGroups = groupReport(comMachine);
1310 const QString strGroupsOld = groupReport(comMachineOld);
1311 if (!strGroups.isNull())
1312 {
1313 ++iRowCount;
1314 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Groups", "details (general)"),
1315 empReport(strGroups, strGroupsOld));
1316 }
1317
1318 break;
1319 }
1320 case DetailsElementType_System:
1321 {
1322 /* Base Memory: */
1323 ++iRowCount;
1324 const QString strMemory = QApplication::translate("UIDetails", "%1 MB", "details").arg(comMachine.GetMemorySize());
1325 const QString strMemoryOld = QApplication::translate("UIDetails", "%1 MB", "details").arg(comMachineOld.GetMemorySize());
1326 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Base Memory", "details (system)"),
1327 empReport(strMemory, strMemoryOld));
1328
1329 /* Processors? */
1330 const int cCpu = comMachine.GetCPUCount();
1331 const int cCpuOld = comMachineOld.GetCPUCount();
1332 if (cCpu > 1)
1333 {
1334 ++iRowCount;
1335 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Processors", "details (system)"),
1336 empReport(QString::number(cCpu), QString::number(cCpuOld)));
1337 }
1338
1339 /* Execution Cap? */
1340 const ULONG uExecutionCap = comMachine.GetCPUExecutionCap();
1341 if (uExecutionCap < 100)
1342 {
1343 ++iRowCount;
1344 const QString strExecutionCap = QApplication::translate("UIDetails", "%1%", "details").arg(uExecutionCap);
1345 const QString strExecutionCapOld = QApplication::translate("UIDetails", "%1%", "details").arg(comMachineOld.GetCPUExecutionCap());
1346 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Execution Cap", "details (system)"),
1347 empReport(strExecutionCap, strExecutionCapOld));
1348 }
1349
1350 /* Boot Order: */
1351 ++iRowCount;
1352 const QString strBootOrder = bootOrderReport(comMachine);
1353 const QString strBootOrderOld = bootOrderReport(comMachineOld);
1354 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Boot Order", "details (system)"),
1355 empReport(strBootOrder, strBootOrderOld));
1356
1357 /* Chipset Type? */
1358 CPlatform comPlatform = comMachine.GetPlatform();
1359 const KChipsetType enmChipsetType = comPlatform.GetChipsetType();
1360 CPlatform comPlatformOld = comMachineOld.GetPlatform();
1361 const KChipsetType enmChipsetTypeOld = comPlatformOld.GetChipsetType();
1362 if (enmChipsetType == KChipsetType_ICH9)
1363 {
1364 ++iRowCount;
1365 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Chipset Type", "details (system)"),
1366 empReport(gpConverter->toString(enmChipsetType),
1367 gpConverter->toString(enmChipsetTypeOld)));
1368 }
1369
1370 /* EFI? */
1371 const QString strEfiState = efiStateReport(comMachine);
1372 const QString strEfiStateOld = efiStateReport(comMachineOld);
1373 if (!strEfiState.isNull())
1374 {
1375 ++iRowCount;
1376 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "EFI", "details (system)"),
1377 empReport(strEfiState, strEfiStateOld));
1378 }
1379
1380 /* Acceleration? */
1381 const QString strAcceleration = accelerationReport(comMachine);
1382 const QString strAccelerationOld = accelerationReport(comMachineOld);
1383 if (!strAcceleration.isNull())
1384 {
1385 ++iRowCount;
1386 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Acceleration", "details (system)"),
1387 empReport(strAcceleration, strAccelerationOld));
1388 }
1389
1390 break;
1391 }
1392 case DetailsElementType_Preview:
1393 {
1394 /* Preview: */
1395 ++iRowCount;
1396 strItem += QString(sSectionItemTpl4).arg("details://thumbnail").arg("#thumbnail");
1397
1398 break;
1399 }
1400 case DetailsElementType_Display:
1401 {
1402 const CGraphicsAdapter &comGraphics = comMachine.GetGraphicsAdapter();
1403 const CGraphicsAdapter &comGraphicsOld = comMachineOld.GetGraphicsAdapter();
1404
1405 /* Video Memory: */
1406 ++iRowCount;
1407 const QString strVram = QApplication::translate("UIDetails", "%1 MB", "details").arg(comGraphics.GetVRAMSize());
1408 const QString strVramOld = QApplication::translate("UIDetails", "%1 MB", "details").arg(comGraphicsOld.GetVRAMSize());
1409 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Video Memory", "details (display)"),
1410 empReport(strVram, strVramOld));
1411
1412 /* Screens? */
1413 const int cScreens = comGraphics.GetMonitorCount();
1414 const int cScreensOld = comGraphicsOld.GetMonitorCount();
1415 if (cScreens > 1)
1416 {
1417 ++iRowCount;
1418 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Screens", "details (display)"),
1419 empReport(QString::number(cScreens), QString::number(cScreensOld)));
1420 }
1421
1422 /* Scale-factor? */
1423 const double uScaleFactor = scaleFactorReport(comMachine);
1424 const double uScaleFactorOld = scaleFactorReport(comMachineOld);
1425 if (uScaleFactor != 1.0)
1426 {
1427 ++iRowCount;
1428 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Scale-factor", "details (display)"),
1429 empReport(QString::number(uScaleFactor, 'f', 2),
1430 QString::number(uScaleFactorOld, 'f', 2)));
1431 }
1432
1433 /* Graphics Controller: */
1434 ++iRowCount;
1435 const QString strGc = gpConverter->toString(comGraphics.GetGraphicsControllerType());
1436 const QString strGcOld = gpConverter->toString(comGraphicsOld.GetGraphicsControllerType());
1437 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Graphics Controller", "details (display)"),
1438 empReport(strGc, strGcOld));
1439
1440 /* Acceleration? */
1441 const QString strAcceleration = displayAccelerationReport(comGraphics);
1442 const QString strAccelerationOld = displayAccelerationReport(comGraphicsOld);
1443 if (!strAcceleration.isNull())
1444 {
1445 ++iRowCount;
1446 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Acceleration", "details (display)"),
1447 empReport(strAcceleration, strAccelerationOld));
1448 }
1449
1450 /* Remote Desktop Server: */
1451 QStringList aVrdeReport = vrdeServerReport(comMachine);
1452 QStringList aVrdeReportOld = vrdeServerReport(comMachineOld);
1453 if (!aVrdeReport.isEmpty())
1454 {
1455 ++iRowCount;
1456 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Remote Desktop Server Port", "details (display/vrde)"),
1457 empReport(aVrdeReport.value(0), aVrdeReportOld.value(0)));
1458 }
1459 else
1460 {
1461 ++iRowCount;
1462 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Remote Desktop Server", "details (display/vrde)"),
1463 empReport(QApplication::translate("UIDetails", "Disabled", "details (display/vrde/VRDE server)"), aVrdeReportOld.isEmpty()));
1464 }
1465
1466 /* Recording: */
1467 QStringList aRecordingReport = recordingReport(comMachine);
1468 QStringList aRecordingReportOld = recordingReport(comMachineOld);
1469 if (!aRecordingReport.isEmpty())
1470 {
1471 ++iRowCount;
1472 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Recording File", "details (display/recording)"),
1473 empReport(aRecordingReport.value(0), aRecordingReportOld.value(0)));
1474 ++iRowCount;
1475 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Recording Attributes", "details (display/recording)"),
1476 empReport(aRecordingReport.value(1), aRecordingReportOld.value(1)));
1477 }
1478 else
1479 {
1480 ++iRowCount;
1481 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Recording", "details (display/recording)"),
1482 empReport(QApplication::translate("UIDetails", "Disabled", "details (display/recording)"), aRecordingReportOld.isEmpty()));
1483 }
1484
1485 break;
1486 }
1487 case DetailsElementType_Storage:
1488 {
1489 /* Storage: */
1490 QPair<QStringList, QList<QMap<QString, QString> > > report = storageReport(comMachine);
1491 QStringList aControllers = report.first;
1492 QList<QMap<QString, QString> > aAttachments = report.second;
1493 QPair<QStringList, QList<QMap<QString, QString> > > reportOld = storageReport(comMachineOld);
1494 QStringList aControllersOld = reportOld.first;
1495 QList<QMap<QString, QString> > aAttachmentsOld = reportOld.second;
1496
1497 /* Iterate through storage controllers: */
1498 for (int i = 0; i < aControllers.size(); ++i)
1499 {
1500 /* Add controller information: */
1501 ++iRowCount;
1502 strItem += QString(sSectionItemTpl3).arg(empReport(aControllers.value(i), aControllersOld.value(i)));
1503
1504 /* Iterate through storage attachments: */
1505 QMap<QString, QString> aCurrentAttachments = aAttachments.value(i);
1506 QMap<QString, QString> aCurrentAttachmentsOld = aAttachmentsOld.value(i);
1507 for (int j = 0; j < aCurrentAttachments.keys().size(); ++j)
1508 {
1509 const QString &strSlotInfo = empReport(aCurrentAttachments.keys().value(j),
1510 aCurrentAttachmentsOld.keys().value(j));
1511 const QString &strMediumInfo = empReport(aCurrentAttachments.value(aCurrentAttachments.keys().value(j)),
1512 aCurrentAttachmentsOld.value(aCurrentAttachments.keys().value(j)));
1513 /* Add attachment information: */
1514 ++iRowCount;
1515 strItem += QString(sSectionItemTpl2).arg(strSlotInfo, strMediumInfo);
1516 }
1517 }
1518
1519 /* Handle side-case: */
1520 if (strItem.isNull())
1521 {
1522 /* Not Attached: */
1523 ++iRowCount;
1524 strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Not Attached", "details (storage)"), aControllersOld.isEmpty()));
1525 }
1526
1527 break;
1528 }
1529 case DetailsElementType_Audio:
1530 {
1531 /* Audio: */
1532 QStringList aReport = audioReport(comMachine);
1533 QStringList aReportOld = audioReport(comMachineOld);
1534
1535 /* If there is something to report: */
1536 if (!aReport.isEmpty())
1537 {
1538 /* Host Driver: */
1539 ++iRowCount;
1540 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Host Driver", "details (audio)"),
1541 empReport(aReport.value(0), aReportOld.value(0)));
1542
1543 /* Controller: */
1544 ++iRowCount;
1545 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Controller", "details (audio)"),
1546 empReport(aReport.value(1), aReportOld.value(1)));
1547
1548#ifdef VBOX_WITH_AUDIO_INOUT_INFO
1549 /* Output: */
1550 ++iRowCount;
1551 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Audio Output", "details (audio)"),
1552 empReport(aReport.value(2), aReportOld.value(2)));
1553
1554 /* Input: */
1555 ++iRowCount;
1556 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Audio Input", "details (audio)"),
1557 empReport(aReport.value(3), aReportOld.value(3)));
1558#endif /* VBOX_WITH_AUDIO_INOUT_INFO */
1559 }
1560
1561 /* Handle side-case: */
1562 if (strItem.isNull())
1563 {
1564 /* Disabled: */
1565 ++iRowCount;
1566 strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Disabled", "details (audio)"), aReportOld.isEmpty()));
1567 }
1568
1569 break;
1570 }
1571 case DetailsElementType_Network:
1572 {
1573 /* Network: */
1574 QStringList aReport = networkReport(comMachine);
1575 QStringList aReportOld = networkReport(comMachineOld);
1576
1577 /* Iterate through network adapters: */
1578 for (int i = 0; i < aReport.size(); ++i)
1579 {
1580 const QString &strAdapterInformation = aReport.value(i);
1581 const QString &strAdapterInformationOld = aReportOld.value(i);
1582 /* Add adapter information: */
1583 ++iRowCount;
1584 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Adapter %1", "details (network)").arg(i + 1),
1585 empReport(strAdapterInformation, strAdapterInformationOld));
1586 }
1587
1588 /* Handle side-case: */
1589 if (strItem.isNull())
1590 {
1591 /* Disabled: */
1592 ++iRowCount;
1593 strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Disabled", "details (network/adapter)"), aReportOld.isEmpty()));
1594 }
1595
1596 break;
1597 }
1598 case DetailsElementType_Serial:
1599 {
1600 /* Serial: */
1601 QStringList aReport = serialReport(comMachine);
1602 QStringList aReportOld = serialReport(comMachineOld);
1603
1604 /* Iterate through serial ports: */
1605 for (int i = 0; i < aReport.size(); ++i)
1606 {
1607 const QString &strPortInformation = aReport.value(i);
1608 const QString &strPortInformationOld = aReportOld.value(i);
1609 /* Add port information: */
1610 ++iRowCount;
1611 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Port %1", "details (serial)").arg(i + 1),
1612 empReport(strPortInformation, strPortInformationOld));
1613 }
1614
1615 /* Handle side-case: */
1616 if (strItem.isNull())
1617 {
1618 /* Disabled: */
1619 ++iRowCount;
1620 strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Disabled", "details (serial)"), aReportOld.isEmpty()));
1621 }
1622
1623 break;
1624 }
1625 case DetailsElementType_USB:
1626 {
1627 /* USB: */
1628 QStringList aReport = usbReport(comMachine);
1629 QStringList aReportOld = usbReport(comMachineOld);
1630
1631 /* If there is something to report: */
1632 if (!aReport.isEmpty())
1633 {
1634 /* USB Controller: */
1635 ++iRowCount;
1636 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "USB Controller", "details (usb)"),
1637 empReport(aReport.value(0), aReportOld.value(0)));
1638
1639 /* Device Filters: */
1640 ++iRowCount;
1641 strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Device Filters", "details (usb)"),
1642 empReport(aReport.value(1), aReportOld.value(1)));
1643 }
1644
1645 /* Handle side-case: */
1646 if (strItem.isNull())
1647 {
1648 /* Disabled: */
1649 ++iRowCount;
1650 strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Disabled", "details (usb)"), aReportOld.isEmpty()));
1651 }
1652
1653 break;
1654 }
1655 case DetailsElementType_SF:
1656 {
1657 /* Shared Folders: */
1658 const ulong cFolders = comMachine.GetSharedFolders().size();
1659 const ulong cFoldersOld = comMachineOld.GetSharedFolders().size();
1660 if (cFolders > 0)
1661 {
1662 ++iRowCount;
1663 strItem = QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Shared Folders", "details (shared folders)"),
1664 empReport(QString::number(cFolders), QString::number(cFoldersOld)));
1665 }
1666 else
1667 {
1668 ++iRowCount;
1669 strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "None", "details (shared folders)"), cFoldersOld == 0));
1670 }
1671
1672 break;
1673 }
1674 default:
1675 break;
1676 }
1677
1678 /* Append report: */
1679 if (enmType != DetailsElementType_Preview || !m_pixmapScreenshot.isNull())
1680 strReport += strSectionTpl
1681 .arg(1 + iRowCount) /* rows */
1682 .arg(QString("details://%1").arg(gpConverter->toInternalString(enmType)), /* icon */
1683 QString::number(iIconArea), /* icon area */
1684 QString("%1:").arg(gpConverter->toString(enmType)), /* title */
1685 strItem /* items */);
1686
1687 /* Return report as table: */
1688 return QString(sTableTpl).arg(strReport);
1689}
1690
1691/* static */
1692QString UISnapshotDetailsWidget::groupReport(const CMachine &comMachine)
1693{
1694 /* Prepare report: */
1695 QStringList aReport = comMachine.GetGroups().toList();
1696 /* Do not show groups for machine which is in root group only: */
1697 if (aReport.size() == 1)
1698 aReport.removeAll("/");
1699 /* For all groups => trim first '/' symbol: */
1700 for (int i = 0; i < aReport.size(); ++i)
1701 {
1702 QString &strGroup = aReport[i];
1703 if (strGroup.startsWith("/") && strGroup != "/")
1704 strGroup.remove(0, 1);
1705 }
1706 /* Compose and return report: */
1707 return aReport.isEmpty() ? QString() : aReport.join(", ");
1708}
1709
1710/* static */
1711QString UISnapshotDetailsWidget::bootOrderReport(const CMachine &comMachine)
1712{
1713 /* Prepare report: */
1714 QStringList aReport;
1715 /* Iterate through boot device types: */
1716 CPlatformProperties comProperties = uiCommon().virtualBox().GetPlatformProperties(KPlatformArchitecture_x86);
1717 for (ulong i = 1; i <= comProperties.GetMaxBootPosition(); ++i)
1718 {
1719 const KDeviceType enmDevice = comMachine.GetBootOrder(i);
1720 if (enmDevice != KDeviceType_Null)
1721 aReport << gpConverter->toString(enmDevice);
1722 }
1723 /* Make sure report contains at least something: */
1724 if (aReport.isEmpty())
1725 aReport << gpConverter->toString(KDeviceType_Null);
1726 /* Compose and return report: */
1727 return aReport.isEmpty() ? QString() : aReport.join(", ");
1728}
1729
1730/* static */
1731QString UISnapshotDetailsWidget::efiStateReport(const CMachine &comMachine)
1732{
1733 /* Prepare report: */
1734 QString strReport;
1735 CFirmwareSettings comFirmwareSettings = comMachine.GetFirmwareSettings();
1736 switch (comFirmwareSettings.GetFirmwareType())
1737 {
1738 case KFirmwareType_EFI:
1739 case KFirmwareType_EFI32:
1740 case KFirmwareType_EFI64:
1741 case KFirmwareType_EFIDUAL:
1742 {
1743 strReport = QApplication::translate("UIDetails", "Enabled", "details (system/EFI)");
1744 break;
1745 }
1746 default:
1747 {
1748 /* strReport = */ QApplication::translate("UIDetails", "Disabled", "details (system/EFI)");
1749 break;
1750 }
1751 }
1752 /* Return report: */
1753 return strReport;
1754}
1755
1756/* static */
1757QString UISnapshotDetailsWidget::accelerationReport(const CMachine &comMachine)
1758{
1759 /* Acquire platform stuff: */
1760 CPlatform comPlatform = comMachine.GetPlatform();
1761
1762 /* Prepare report: */
1763 QStringList aReport;
1764 switch (comPlatform.GetArchitecture())
1765 {
1766 case KPlatformArchitecture_x86:
1767 {
1768 CPlatformX86 comPlatformX86 = comPlatform.GetX86();
1769
1770 /* VT-x/AMD-V and Nested Paging? */
1771 if (uiCommon().host().GetProcessorFeature(KProcessorFeature_HWVirtEx))
1772 {
1773 /* VT-x/AMD-V? */
1774 if (comPlatformX86.GetHWVirtExProperty(KHWVirtExPropertyType_Enabled))
1775 {
1776 aReport << QApplication::translate("UIDetails", "VT-x/AMD-V", "details (system)");
1777 /* Nested Paging? */
1778 if (comPlatformX86.GetHWVirtExProperty(KHWVirtExPropertyType_NestedPaging))
1779 aReport << QApplication::translate("UIDetails", "Nested Paging", "details (system)");
1780 }
1781 }
1782 /* PAE/NX? */
1783 if (comPlatformX86.GetCPUProperty(KCPUPropertyTypeX86_PAE))
1784 aReport << QApplication::translate("UIDetails", "PAE/NX", "details (system)");
1785
1786 break;
1787 }
1788
1789#ifdef VBOX_WITH_VIRT_ARMV8
1790 case KPlatformArchitecture_ARM:
1791 {
1792 /** @todo BUGBUG ARM stuff goes here. */
1793 break;
1794 }
1795#endif
1796
1797 default:
1798 break;
1799 }
1800 /* Paravirtualization Interface? */
1801 switch (comMachine.GetEffectiveParavirtProvider())
1802 {
1803 case KParavirtProvider_Minimal: aReport << QApplication::translate("UIDetails", "Minimal Paravirtualization", "details (system)"); break;
1804 case KParavirtProvider_HyperV: aReport << QApplication::translate("UIDetails", "Hyper-V Paravirtualization", "details (system)"); break;
1805 case KParavirtProvider_KVM: aReport << QApplication::translate("UIDetails", "KVM Paravirtualization", "details (system)"); break;
1806 default: break;
1807 }
1808 /* Compose and return report: */
1809 return aReport.isEmpty() ? QString() : aReport.join(", ");
1810}
1811
1812/* static */
1813double UISnapshotDetailsWidget::scaleFactorReport(CMachine comMachine)
1814{
1815 // WORKAROUND:
1816 // IMachine::GetExtraData still non-const..
1817 CMachine comExtraDataMachine = comMachine;
1818 /* Prepare report: */
1819 const QString strScaleFactor = comExtraDataMachine.GetExtraData(UIExtraDataDefs::GUI_ScaleFactor);
1820 /* Try to convert loaded data to double: */
1821 bool fOk = false;
1822 double dReport = strScaleFactor.toDouble(&fOk);
1823 /* Invent the default value: */
1824 if (!fOk || !dReport)
1825 dReport = 1.0;
1826 /* Return report: */
1827 return dReport;
1828}
1829
1830/* static */
1831QString UISnapshotDetailsWidget::displayAccelerationReport(CGraphicsAdapter comGraphics)
1832{
1833 /* Prepare report: */
1834 QStringList aReport;
1835 /* 3D Acceleration? */
1836 if (comGraphics.GetAccelerate3DEnabled())
1837 aReport << QApplication::translate("UIDetails", "3D", "details (display)");
1838 /* Compose and return report: */
1839 return aReport.isEmpty() ? QString() : aReport.join(", ");
1840}
1841
1842/* static */
1843QStringList UISnapshotDetailsWidget::vrdeServerReport(CMachine comMachine)
1844{
1845 /* Prepare report: */
1846 QStringList aReport;
1847 /* Acquire VRDE server: */
1848 const CVRDEServer &comServer = comMachine.GetVRDEServer();
1849 if (comServer.GetEnabled())
1850 {
1851 /* Remote Desktop Server Port: */
1852 aReport << comServer.GetVRDEProperty("TCP/Ports");
1853 }
1854 /* Return report: */
1855 return aReport;
1856}
1857
1858/* static */
1859QStringList UISnapshotDetailsWidget::recordingReport(CMachine comMachine)
1860{
1861 /* Prepare report: */
1862 QStringList aReport;
1863 /* Acquire recording status: */
1864 CRecordingSettings comRecordingSettings = comMachine.GetRecordingSettings();
1865 /* For now all screens have the same config: */
1866 CRecordingScreenSettings comRecordingScreen0Settings = comRecordingSettings.GetScreenSettings(0);
1867 if (comRecordingScreen0Settings.GetEnabled())
1868 {
1869 /* Recording file: */
1870 aReport << comRecordingScreen0Settings.GetFilename();
1871 /* Recording attributes: */
1872 aReport << QApplication::translate("UIDetails", "Frame Size: %1x%2, Frame Rate: %3fps, Bit Rate: %4kbps")
1873 .arg(comRecordingScreen0Settings.GetVideoWidth())
1874 .arg(comRecordingScreen0Settings.GetVideoHeight())
1875 .arg(comRecordingScreen0Settings.GetVideoFPS())
1876 .arg(comRecordingScreen0Settings.GetVideoRate());
1877 }
1878 /* Return report: */
1879 return aReport;
1880}
1881
1882/* static */
1883QPair<QStringList, QList<QMap<QString, QString> > > UISnapshotDetailsWidget::storageReport(CMachine comMachine)
1884{
1885 /* Prepare report: */
1886 QStringList aControllers;
1887 QList<QMap<QString, QString> > aAttachments;
1888 /* Iterate through machine storage controllers: */
1889 foreach (const CStorageController &comController, comMachine.GetStorageControllers())
1890 {
1891 /* Append controller information: */
1892 aControllers << QApplication::translate("UIMachineSettingsStorage", "Controller: %1").arg(comController.GetName());
1893
1894 /* Prepare attachment information: */
1895 QMap<QString, QString> mapAttachments;
1896 /* Iterate through machine storage attachments: */
1897 foreach (const CMediumAttachment &comAttachment, comMachine.GetMediumAttachmentsOfController(comController.GetName()))
1898 {
1899 /* Prepare current slot information: */
1900 const QString strSlotInfo = QString("&nbsp;&nbsp;")
1901 + gpConverter->toString(StorageSlot(comController.GetBus(),
1902 comAttachment.GetPort(),
1903 comAttachment.GetDevice()))
1904 + ( comAttachment.GetType() == KDeviceType_DVD
1905 ? QApplication::translate("UIDetails", "[Optical Drive]", "details (storage)").prepend(' ')
1906 : QString());
1907
1908 /* Prepare current medium information: */
1909 const QString strMediumInfo = comAttachment.isOk()
1910 ? wipeHtmlStuff(uiCommon().storageDetails(comAttachment.GetMedium(), false))
1911 : QString();
1912
1913 /* Cache current slot/medium information: */
1914 if (!strMediumInfo.isNull())
1915 mapAttachments.insert(strSlotInfo, strMediumInfo);
1916 }
1917 /* Append attachment information: */
1918 aAttachments << mapAttachments;
1919 }
1920 /* Compose and return report: */
1921 return qMakePair(aControllers, aAttachments);
1922}
1923
1924/* static */
1925QStringList UISnapshotDetailsWidget::audioReport(CMachine comMachine)
1926{
1927 /* Prepare report: */
1928 QStringList aReport;
1929 /* Acquire audio adapter: */
1930 const CAudioSettings comAudioSettings = comMachine.GetAudioSettings();
1931 const CAudioAdapter &comAdapter = comAudioSettings.GetAdapter();
1932 if (comAdapter.GetEnabled())
1933 {
1934 /* Host Driver: */
1935 aReport << gpConverter->toString(comAdapter.GetAudioDriver());
1936
1937 /* Controller: */
1938 aReport << gpConverter->toString(comAdapter.GetAudioController());
1939
1940#ifdef VBOX_WITH_AUDIO_INOUT_INFO
1941 /* Output: */
1942 aReport << ( comAdapter.GetEnabledOut()
1943 ? QApplication::translate("UIDetails", "Enabled", "details (audio/output)")
1944 : QApplication::translate("UIDetails", "Disabled", "details (audio/output)"));
1945
1946 /* Input: */
1947 aReport << ( comAdapter.GetEnabledIn()
1948 ? QApplication::translate("UIDetails", "Enabled", "details (audio/input)")
1949 : QApplication::translate("UIDetails", "Disabled", "details (audio/input)"));
1950#endif /* VBOX_WITH_AUDIO_INOUT_INFO */
1951 }
1952 /* Return report: */
1953 return aReport;
1954}
1955
1956/* static */
1957QStringList UISnapshotDetailsWidget::networkReport(CMachine comMachine)
1958{
1959 /* Prepare report: */
1960 QStringList aReport;
1961 /* Iterate through machine network adapters: */
1962 CPlatformProperties comProperties = uiCommon().virtualBox().GetPlatformProperties(KPlatformArchitecture_x86);
1963 CPlatform comPlatform = comMachine.GetPlatform();
1964 const ulong iCount = comProperties.GetMaxNetworkAdapters(comPlatform.GetChipsetType());
1965 for (ulong iSlot = 0; iSlot < iCount; ++iSlot)
1966 {
1967 /* Get current network adapter: */
1968 const CNetworkAdapter &comAdapter = comMachine.GetNetworkAdapter(iSlot);
1969 if (comAdapter.GetEnabled())
1970 {
1971 /* Use adapter type string as template: */
1972 QString strInfo = gpConverter->toString(comAdapter.GetAdapterType()).replace(QRegularExpression("\\s\\(.+\\)"), " (%1)");
1973 /* Don't use the adapter type string for types that have an additional
1974 * symbolic network/interface name field, use this name instead: */
1975 const KNetworkAttachmentType enmType = comAdapter.GetAttachmentType();
1976 switch (enmType)
1977 {
1978 case KNetworkAttachmentType_Bridged:
1979 strInfo = strInfo.arg(QApplication::translate("UIDetails", "Bridged Adapter, %1", "details (network)")
1980 .arg(comAdapter.GetBridgedInterface()));
1981 break;
1982 case KNetworkAttachmentType_Internal:
1983 strInfo = strInfo.arg(QApplication::translate("UIDetails", "Internal Network, '%1'", "details (network)")
1984 .arg(comAdapter.GetInternalNetwork()));
1985 break;
1986 case KNetworkAttachmentType_HostOnly:
1987 strInfo = strInfo.arg(QApplication::translate("UIDetails", "Host-only Adapter, '%1'", "details (network)")
1988 .arg(comAdapter.GetHostOnlyInterface()));
1989 break;
1990 case KNetworkAttachmentType_Generic:
1991 {
1992 QString strGenericDriverProperties(summarizeGenericProperties(comAdapter));
1993 strInfo = strInfo.arg( strGenericDriverProperties.isNull()
1994 ? strInfo.arg(QApplication::translate("UIDetails", "Generic Driver, '%1'", "details (network)")
1995 .arg(comAdapter.GetGenericDriver()))
1996 : strInfo.arg(QApplication::translate("UIDetails", "Generic Driver, '%1' { %2 }", "details (network)")
1997 .arg(comAdapter.GetGenericDriver(), strGenericDriverProperties)));
1998 break;
1999 }
2000 case KNetworkAttachmentType_NATNetwork:
2001 strInfo = strInfo.arg(QApplication::translate("UIDetails", "NAT Network, '%1'", "details (network)")
2002 .arg(comAdapter.GetNATNetwork()));
2003 break;
2004 default:
2005 strInfo = strInfo.arg(gpConverter->toString(enmType));
2006 break;
2007 }
2008 /* Append adapter information: */
2009 aReport << strInfo;
2010 }
2011 }
2012 /* Return report: */
2013 return aReport;
2014}
2015
2016/* static */
2017QStringList UISnapshotDetailsWidget::serialReport(CMachine comMachine)
2018{
2019 /* Prepare report: */
2020 QStringList aReport;
2021 /* Iterate through machine serial ports: */
2022 CPlatformProperties comProperties = uiCommon().virtualBox().GetPlatformProperties(KPlatformArchitecture_x86);
2023 const ulong iCount = comProperties.GetSerialPortCount();
2024 for (ulong iSlot = 0; iSlot < iCount; ++iSlot)
2025 {
2026 /* Get current serial port: */
2027 const CSerialPort &comPort = comMachine.GetSerialPort(iSlot);
2028 if (comPort.GetEnabled())
2029 {
2030 /* Determine port mode: */
2031 const KPortMode enmMode = comPort.GetHostMode();
2032 /* Compose the data: */
2033 QStringList aInfo;
2034 aInfo << UITranslator::toCOMPortName(comPort.GetIRQ(), comPort.GetIOAddress());
2035 if ( enmMode == KPortMode_HostPipe
2036 || enmMode == KPortMode_HostDevice
2037 || enmMode == KPortMode_TCP
2038 || enmMode == KPortMode_RawFile)
2039 aInfo << QString("%1 (<nobr>%2</nobr>)")
2040 .arg(gpConverter->toString(enmMode))
2041 .arg(QDir::toNativeSeparators(comPort.GetPath()));
2042 else
2043 aInfo << gpConverter->toString(enmMode);
2044 /* Append port information: */
2045 aReport << aInfo.join(", ");
2046 }
2047 }
2048 /* Return report: */
2049 return aReport;
2050}
2051
2052/* static */
2053QStringList UISnapshotDetailsWidget::usbReport(CMachine comMachine)
2054{
2055 /* Prepare report: */
2056 QStringList aReport;
2057 /* Acquire USB filters object: */
2058 const CUSBDeviceFilters &comFiltersObject = comMachine.GetUSBDeviceFilters();
2059 if ( !comFiltersObject.isNull()
2060 && comMachine.GetUSBProxyAvailable())
2061 {
2062 /* Acquire USB controllers: */
2063 const CUSBControllerVector aControllers = comMachine.GetUSBControllers();
2064 if (!aControllers.isEmpty())
2065 {
2066 /* USB Controller: */
2067 QStringList aControllerList;
2068 foreach (const CUSBController &comController, aControllers)
2069 aControllerList << gpConverter->toString(comController.GetType());
2070 aReport << aControllerList.join(", ");
2071
2072 /* Device Filters: */
2073 const CUSBDeviceFilterVector &aFilters = comFiltersObject.GetDeviceFilters();
2074 uint cActive = 0;
2075 foreach (const CUSBDeviceFilter &comFilter, aFilters)
2076 if (comFilter.GetActive())
2077 ++cActive;
2078 aReport << QApplication::translate("UIDetails", "%1 (%2 active)", "details (usb)")
2079 .arg(aFilters.size()).arg(cActive);
2080 }
2081 }
2082 /* Return report: */
2083 return aReport;
2084}
2085
2086/* static */
2087QString UISnapshotDetailsWidget::wipeHtmlStuff(const QString &strString)
2088{
2089 return QString(strString).remove(QRegularExpression("<i>|</i>|<b>|</b>"));
2090}
2091
2092/* static */
2093QString UISnapshotDetailsWidget::empReport(const QString &strValue, const QString &strOldValue)
2094{
2095 return strValue == strOldValue ? strValue : QString("<u>%1</u>").arg(strValue);
2096}
2097
2098/* static */
2099QString UISnapshotDetailsWidget::empReport(const QString &strValue, bool fIgnore)
2100{
2101 return fIgnore ? strValue : QString("<u>%1</u>").arg(strValue);
2102}
2103
2104/* static */
2105QString UISnapshotDetailsWidget::summarizeGenericProperties(const CNetworkAdapter &comNetwork)
2106{
2107 QVector<QString> names;
2108 QVector<QString> props;
2109 props = comNetwork.GetProperties(QString(), names);
2110 QString strResult;
2111 for (int i = 0; i < names.size(); ++i)
2112 {
2113 strResult += names[i] + "=" + props[i];
2114 if (i < names.size() - 1)
2115 strResult += ", ";
2116 }
2117 return strResult;
2118}
2119
2120#include "UISnapshotDetailsWidget.moc"
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