VirtualBox

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

Last change on this file was 104358, checked in by vboxsync, 4 weeks ago

FE/Qt. bugref:10622. More refactoring around the retranslation functionality.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use