VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

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