VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.cpp@ 102493

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

FE/Qt: bugref:10450: Get rid of Qt5 stuff; This one is about replacing obsolete stuff.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 190.7 KB
RevLine 
[30677]1/* $Id: UIExtraDataManager.cpp 101571 2023-10-24 00:48:20Z vboxsync $ */
2/** @file
[51038]3 * VBox Qt GUI - UIExtraDataManager class implementation.
[30677]4 */
5
6/*
[98103]7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
[30677]8 *
[96407]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
[30677]26 */
27
[41587]28/* Qt includes: */
[76606]29#include <QFontDatabase>
30#include <QMetaEnum>
31#include <QMutex>
[93996]32#include <QRegExp>
[76606]33#include <QRegularExpression>
[93996]34#include <QRegularExpressionValidator>
[60599]35#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
[76606]36# include <QComboBox>
37# include <QHeaderView>
38# include <QLabel>
39# include <QLineEdit>
40# include <QListView>
[81272]41# include <QMainWindow>
[76606]42# include <QMenuBar>
43# include <QPainter>
44# include <QPushButton>
45# include <QSortFilterProxyModel>
46# include <QStyledItemDelegate>
47# include <QTableView>
48# include <QVBoxLayout>
[52733]49# include <QStandardItemModel>
50# include <QXmlStreamWriter>
51# include <QXmlStreamReader>
[60599]52#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
[52730]53
[76606]54/* GUI includes: */
[79365]55#include "UICommon.h"
[76606]56#include "UIActionPool.h"
57#include "UIConverter.h"
58#include "UIDesktopWidgetWatchdog.h"
59#include "UIExtraDataManager.h"
60#include "UIHostComboEditor.h"
61#include "UIMainEventListener.h"
62#include "UIMessageCenter.h"
63#include "UISettingsDefs.h"
64#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
65# include "QIDialog.h"
66# include "QIDialogButtonBox.h"
67# include "QIFileDialog.h"
68# include "QISplitter.h"
69# include "QIWidgetValidator.h"
[81261]70# include "QIWithRestorableGeometry.h"
[76606]71# include "VBoxUtils.h"
72# include "UIIconPool.h"
[86233]73# include "QIToolBar.h"
[76606]74# include "UIVirtualBoxEventHandler.h"
75#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
[52733]76
[76606]77/* COM includes: */
78#include "COMEnums.h"
79#include "CEventListener.h"
80#include "CEventSource.h"
81#include "CMachine.h"
82#include "CVirtualBox.h"
83
84
[51668]85/* Namespaces: */
86using namespace UIExtraDataDefs;
[54794]87using namespace UISettingsDefs;
[51038]88
[51668]89
[60222]90/** Private QObject extension
91 * providing UIExtraDataManager with the CVirtualBox event-source. */
[51038]92class UIExtraDataEventHandler : public QObject
[30677]93{
94 Q_OBJECT;
95
[51038]96signals:
97
[51177]98 /** Notifies about 'extra-data change' event: */
[74942]99 void sigExtraDataChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue);
[51038]100
[30677]101public:
[44453]102
[60227]103 /** Constructs event proxy object on the basis of passed @a pParent. */
[51038]104 UIExtraDataEventHandler(QObject *pParent);
[60227]105 /** Destructs event proxy object. */
106 ~UIExtraDataEventHandler();
[30677]107
[60227]108protected slots:
[30677]109
[51177]110 /** Preprocess 'extra-data can change' event: */
[74942]111 void sltPreprocessExtraDataCanChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue, bool &fVeto, QString &strVetoReason);
[51177]112 /** Preprocess 'extra-data change' event: */
[74942]113 void sltPreprocessExtraDataChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue);
[51038]114
[60227]115protected:
116
117 /** @name Prepare/Cleanup cascade.
118 * @{ */
119 /** Prepares all. */
120 void prepare();
121 /** Prepares listener. */
122 void prepareListener();
123 /** Prepares connections. */
124 void prepareConnections();
125
126 /** Cleanups connections. */
127 void cleanupConnections();
128 /** Cleanups listener. */
129 void cleanupListener();
130 /** Cleanups all. */
131 void cleanup();
132 /** @} */
133
[51038]134private:
135
[60227]136 /** Holds the Qt event listener instance. */
137 ComObjPtr<UIMainEventListenerImpl> m_pQtListener;
138 /** Holds the COM event listener instance. */
139 CEventListener m_comEventListener;
140
[51177]141 /** Protects sltPreprocessExtraDataChange. */
[51038]142 QMutex m_mutex;
143};
144
[71437]145
146/*********************************************************************************************************************************
147* Class UIExtraDataEventHandler implementation. *
148*********************************************************************************************************************************/
149
[51038]150UIExtraDataEventHandler::UIExtraDataEventHandler(QObject *pParent)
151 : QObject(pParent)
[51177]152{
[60227]153 /* Prepare: */
154 prepare();
[51177]155}
[51038]156
[60227]157UIExtraDataEventHandler::~UIExtraDataEventHandler()
158{
159 /* Cleanup: */
160 cleanup();
161}
162
163void UIExtraDataEventHandler::prepare()
164{
165 /* Prepare: */
166 prepareListener();
167 prepareConnections();
168}
169
170void UIExtraDataEventHandler::prepareListener()
171{
172 /* Create event listener instance: */
173 m_pQtListener.createObject();
174 m_pQtListener->init(new UIMainEventListener, this);
175 m_comEventListener = CEventListener(m_pQtListener);
176
177 /* Get VirtualBox: */
[79365]178 const CVirtualBox comVBox = uiCommon().virtualBox();
[63626]179 AssertWrapperOk(comVBox);
180 /* Get VirtualBox event source: */
181 CEventSource comEventSourceVBox = comVBox.GetEventSource();
182 AssertWrapperOk(comEventSourceVBox);
183
184 /* Enumerate all the required event-types: */
185 QVector<KVBoxEventType> eventTypes;
186 eventTypes
[60227]187 << KVBoxEventType_OnExtraDataCanChange
188 << KVBoxEventType_OnExtraDataChanged;
[63626]189
190 /* Register event listener for VirtualBox event source: */
[86541]191 comEventSourceVBox.RegisterListener(m_comEventListener, eventTypes, FALSE /* active? */);
[63626]192 AssertWrapperOk(comEventSourceVBox);
[60260]193
[86541]194 /* Register event sources in their listeners as well: */
195 m_pQtListener->getWrapped()->registerSource(comEventSourceVBox, m_comEventListener);
[60227]196}
197
198void UIExtraDataEventHandler::prepareConnections()
199{
200 /* Create direct (sync) connections for signals of main listener: */
[68348]201 connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigExtraDataCanChange,
202 this, &UIExtraDataEventHandler::sltPreprocessExtraDataCanChange,
[60227]203 Qt::DirectConnection);
[68348]204 connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigExtraDataChange,
205 this, &UIExtraDataEventHandler::sltPreprocessExtraDataChange,
[60227]206 Qt::DirectConnection);
207}
208
209void UIExtraDataEventHandler::cleanupConnections()
210{
211 /* Nothing for now. */
212}
213
214void UIExtraDataEventHandler::cleanupListener()
215{
[86541]216 /* Unregister everything: */
217 m_pQtListener->getWrapped()->unregisterSources();
[60260]218
[60227]219 /* Make sure VBoxSVC is available: */
[79365]220 if (!uiCommon().isVBoxSVCAvailable())
[60227]221 return;
222
[63626]223 /* Get VirtualBox: */
[79365]224 const CVirtualBox comVBox = uiCommon().virtualBox();
[63626]225 AssertWrapperOk(comVBox);
226 /* Get VirtualBox event source: */
227 CEventSource comEventSourceVBox = comVBox.GetEventSource();
228 AssertWrapperOk(comEventSourceVBox);
229
230 /* Unregister event listener for VirtualBox event source: */
231 comEventSourceVBox.UnregisterListener(m_comEventListener);
[60227]232}
233
234void UIExtraDataEventHandler::cleanup()
235{
236 /* Cleanup: */
237 cleanupConnections();
238 cleanupListener();
239}
240
[74942]241void UIExtraDataEventHandler::sltPreprocessExtraDataCanChange(const QUuid &uMachineID, const QString &strKey, const QString &/* strValue */, bool & /* fVeto */, QString & /* strVetoReason */)
[51038]242{
[51177]243 /* Preprocess global 'extra-data can change' event: */
[74942]244 if (uMachineID.isNull())
[30677]245 {
[51038]246 if (strKey.startsWith("GUI/"))
[30677]247 {
[66593]248 /* Check whether global extra-data property can be applied: */
[69260]249 /// @todo Here can be various extra-data flags handling.
[66593]250 // Generally we should check whether one or another flag feats some rule (like reg-exp).
251 // For each required strValue we should set fVeto = true; and fill strVetoReason = "with some text".
[30677]252 }
253 }
[51038]254}
[30677]255
[74942]256void UIExtraDataEventHandler::sltPreprocessExtraDataChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue)
[51038]257{
[51177]258 /* Preprocess global 'extra-data change' event: */
[74942]259 if (uMachineID.isNull())
[44453]260 {
[51038]261 if (strKey.startsWith("GUI/"))
[44453]262 {
[66593]263 /* Apply global extra-data property: */
[69260]264 /// @todo Here can be various extra-data flags handling.
[66593]265 // Generally we should push one or another flag to various instances which want to handle
266 // those flags independently from UIExtraDataManager. Remember to process each required strValue
267 // from under the m_mutex lock (since we are in another thread) and unlock that m_mutex afterwards.
[44453]268 }
[51038]269 }
[51177]270
271 /* Motify listener about 'extra-data change' event: */
[74942]272 emit sigExtraDataChange(uMachineID, strKey, strValue);
[51038]273}
[30677]274
[44453]275
[60599]276#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
[71437]277
[51931]278/** Data fields. */
279enum Field
280{
281 Field_ID = Qt::UserRole + 1,
282 Field_Name,
283 Field_OsTypeID,
284 Field_Known
285};
286
287
288/** QStyledItemDelegate extension
289 * reflecting items of Extra Data Manager window: Chooser pane. */
290class UIChooserPaneDelegate : public QStyledItemDelegate
291{
292 Q_OBJECT;
293
294public:
295
296 /** Constructor. */
297 UIChooserPaneDelegate(QObject *pParent);
298
299private:
300
301 /** Size-hint calculation routine. */
302 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
303
304 /** Paint routine. */
305 void paint(QPainter *pPainter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
306
307 /** Fetch pixmap info for passed QModelIndex. */
308 static void fetchPixmapInfo(const QModelIndex &index, QPixmap &pixmap, QSize &pixmapSize);
309
310 /** Margin. */
311 int m_iMargin;
312 /** Spacing. */
313 int m_iSpacing;
314};
315
[71437]316
317/*********************************************************************************************************************************
318* Class UIChooserPaneDelegate implementation. *
319*********************************************************************************************************************************/
320
[51931]321UIChooserPaneDelegate::UIChooserPaneDelegate(QObject *pParent)
322 : QStyledItemDelegate(pParent)
323 , m_iMargin(3)
324 , m_iSpacing(3)
325{
326}
327
328QSize UIChooserPaneDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
329{
330 /* Font metrics: */
331 const QFontMetrics &fm = option.fontMetrics;
332 /* Pixmap: */
333 QPixmap pixmap;
334 QSize pixmapSize;
335 fetchPixmapInfo(index, pixmap, pixmapSize);
336
337 /* Calculate width: */
338 const int iWidth = m_iMargin +
339 pixmapSize.width() +
340 2 * m_iSpacing +
[93998]341#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
342 qMax(fm.horizontalAdvance(index.data(Field_Name).toString()),
343 fm.horizontalAdvance(index.data(Field_ID).toString())) +
344#else
[51931]345 qMax(fm.width(index.data(Field_Name).toString()),
346 fm.width(index.data(Field_ID).toString())) +
[93998]347#endif
[51931]348 m_iMargin;
349 /* Calculate height: */
350 const int iHeight = m_iMargin +
351 qMax(pixmapSize.height(),
352 fm.height() + m_iSpacing + fm.height()) +
353 m_iMargin;
354
355 /* Return result: */
356 return QSize(iWidth, iHeight);
357}
358
359void UIChooserPaneDelegate::paint(QPainter *pPainter, const QStyleOptionViewItem &option, const QModelIndex &index) const
360{
361 /* Item rect: */
362 const QRect &optionRect = option.rect;
363 /* Palette: */
364 const QPalette &palette = option.palette;
365 /* Font metrics: */
366 const QFontMetrics &fm = option.fontMetrics;
367 /* Pixmap: */
368 QPixmap pixmap;
369 QSize pixmapSize;
370 fetchPixmapInfo(index, pixmap, pixmapSize);
371
372 /* If item selected: */
373 if (option.state & QStyle::State_Selected)
374 {
375 /* Fill background with selection color: */
376 QColor highlight = palette.color(option.state & QStyle::State_Active ?
377 QPalette::Active : QPalette::Inactive,
378 QPalette::Highlight);
379 QLinearGradient bgGrad(optionRect.topLeft(), optionRect.bottomLeft());
380 bgGrad.setColorAt(0, highlight.lighter(120));
381 bgGrad.setColorAt(1, highlight);
382 pPainter->fillRect(optionRect, bgGrad);
383 /* Draw focus frame: */
384 QStyleOptionFocusRect focusOption;
385 focusOption.rect = optionRect;
386 QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOption, pPainter);
387 }
388
389 /* Draw pixmap: */
390 const QPoint pixmapOrigin = optionRect.topLeft() +
391 QPoint(m_iMargin, m_iMargin);
392 pPainter->drawPixmap(pixmapOrigin, pixmap);
393
394 /* Is that known item? */
395 bool fKnown = index.data(Field_Known).toBool();
396 if (fKnown)
397 {
398 pPainter->save();
399 QFont font = pPainter->font();
400 font.setBold(true);
401 pPainter->setFont(font);
402 }
403
404 /* Draw item name: */
405 const QPoint nameOrigin = pixmapOrigin +
406 QPoint(pixmapSize.width(), 0) +
407 QPoint(2 * m_iSpacing, 0) +
408 QPoint(0, fm.ascent());
409 pPainter->drawText(nameOrigin, index.data(Field_Name).toString());
410
411 /* Was that known item? */
412 if (fKnown)
413 pPainter->restore();
414
415 /* Draw item ID: */
416 const QPoint idOrigin = nameOrigin +
417 QPoint(0, m_iSpacing) +
418 QPoint(0, fm.height());
419 pPainter->drawText(idOrigin, index.data(Field_ID).toString());
420}
421
[30677]422/* static */
[51931]423void UIChooserPaneDelegate::fetchPixmapInfo(const QModelIndex &index, QPixmap &pixmap, QSize &pixmapSize)
424{
425 /* If proper machine ID passed => return corresponding pixmap/size: */
[74878]426 if (index.data(Field_ID).toUuid() != UIExtraDataManager::GlobalID)
[91125]427 pixmap = generalIconPool().guestOSTypePixmapDefault(index.data(Field_OsTypeID).toString(), &pixmapSize);
[51931]428 else
429 {
430 /* For global ID we return static pixmap/size: */
[72696]431 const QIcon icon = UIIconPool::iconSet(":/edata_global_32px.png");
[71006]432 pixmapSize = icon.availableSizes().value(0, QSize(32, 32));
[51931]433 pixmap = icon.pixmap(pixmapSize);
434 }
435}
436
437
438/** QSortFilterProxyModel extension
439 * used by the chooser-pane of the UIExtraDataManagerWindow. */
440class UIChooserPaneSortingModel : public QSortFilterProxyModel
441{
442 Q_OBJECT;
443
444public:
445
446 /** Constructor, passes @a pParent to the QIRichToolButton constructor. */
447 UIChooserPaneSortingModel(QObject *pParent) : QSortFilterProxyModel(pParent) {}
448
449protected:
450
451 /** Returns true if the value of the item referred to by the given index left
452 * is less than the value of the item referred to by the given index right,
453 * otherwise returns false. */
454 bool lessThan(const QModelIndex &leftIdx, const QModelIndex &rightIdx) const
455 {
456 /* Compare by ID first: */
[74878]457 const QUuid strID1 = leftIdx.data(Field_ID).toUuid();
458 const QUuid strID2 = rightIdx.data(Field_ID).toUuid();
[51931]459 if (strID1 == UIExtraDataManager::GlobalID)
460 return true;
461 else if (strID2 == UIExtraDataManager::GlobalID)
462 return false;
463 /* Compare role finally: */
464 return QSortFilterProxyModel::lessThan(leftIdx, rightIdx);
465 }
466};
467
468
[81261]469/** QMainWindow extension
[51931]470 * providing Extra Data Manager with UI features. */
[81261]471class UIExtraDataManagerWindow : public QIWithRestorableGeometry<QMainWindow>
[51931]472{
473 Q_OBJECT;
474
475public:
476
477 /** @name Constructor/Destructor
478 * @{ */
479 /** Extra-data Manager Window constructor. */
[81357]480 UIExtraDataManagerWindow(QWidget *pCenterWidget);
[51931]481 /** Extra-data Manager Window destructor. */
482 ~UIExtraDataManagerWindow();
483 /** @} */
484
485 /** @name Management
486 * @{ */
487 /** Show and raise. */
488 void showAndRaise(QWidget *pCenterWidget);
489 /** @} */
490
[68348]491public slots:
[51931]492
493 /** @name General
494 * @{ */
[51953]495 /** Handles extra-data map acknowledging. */
[74942]496 void sltExtraDataMapAcknowledging(const QUuid &uID);
[51931]497 /** Handles extra-data change. */
[74942]498 void sltExtraDataChange(const QUuid &uID, const QString &strKey, const QString &strValue);
[51931]499 /** @} */
500
[68348]501private slots:
502
503 /** @name General
504 * @{ */
505 /** Handles machine (un)registration. */
[74942]506 void sltMachineRegistered(const QUuid &uID, bool fAdded);
[68348]507 /** @} */
508
[51931]509 /** @name Chooser-pane
510 * @{ */
511 /** Handles filter-apply signal for the chooser-pane. */
512 void sltChooserApplyFilter(const QString &strFilter);
513 /** Handles current-changed signal for the chooser-pane: */
514 void sltChooserHandleCurrentChanged(const QModelIndex &index);
515 /** Handles item-selection-changed signal for the chooser-pane: */
516 void sltChooserHandleSelectionChanged(const QItemSelection &selected,
517 const QItemSelection &deselected);
518 /** @} */
519
520 /** @name Data-pane
521 * @{ */
522 /** Handles filter-apply signal for the data-pane. */
523 void sltDataApplyFilter(const QString &strFilter);
524 /** Handles item-selection-changed signal for the data-pane: */
525 void sltDataHandleSelectionChanged(const QItemSelection &selected,
526 const QItemSelection &deselected);
527 /** Handles item-changed signal for the data-pane: */
528 void sltDataHandleItemChanged(QStandardItem *pItem);
529 /** Handles context-menu-requested signal for the data-pane: */
530 void sltDataHandleCustomContextMenuRequested(const QPoint &pos);
531 /** @} */
532
533 /** @name Actions
534 * @{ */
535 /** Add handler. */
536 void sltAdd();
537 /** Remove handler. */
538 void sltDel();
539 /** Save handler. */
540 void sltSave();
541 /** Load handler. */
542 void sltLoad();
543 /** @} */
544
545private:
546
[63291]547 /** @name General
548 * @{ */
549 /** Returns whether the window should be maximized when geometry being restored. */
[93990]550 virtual bool shouldBeMaximized() const RT_OVERRIDE;
[63291]551 /** @} */
552
[51931]553 /** @name Prepare/Cleanup
554 * @{ */
555 /** Prepare instance. */
556 void prepare();
557 /** Prepare this. */
558 void prepareThis();
559 /** Prepare connections. */
560 void prepareConnections();
561 /** Prepare menu. */
562 void prepareMenu();
563 /** Prepare central widget. */
564 void prepareCentralWidget();
565 /** Prepare tool-bar. */
566 void prepareToolBar();
567 /** Prepare splitter. */
568 void prepareSplitter();
569 /** Prepare panes: */
570 void preparePanes();
571 /** Prepare chooser pane. */
572 void preparePaneChooser();
573 /** Prepare data pane. */
574 void preparePaneData();
575 /** Prepare button-box. */
576 void prepareButtonBox();
577 /** Load window settings. */
578 void loadSettings();
579
580 /** Save window settings. */
581 void saveSettings();
582 /** Cleanup instance. */
583 void cleanup();
584 /** @} */
585
586 /** @name Actions
587 * @{ */
[85389]588 /** Updates action availability. */
[51931]589 void updateActionsAvailability();
590 /** @} */
591
592 /** @name Chooser-pane
593 * @{ */
594 /** Returns chooser index for @a iRow. */
595 QModelIndex chooserIndex(int iRow) const;
596 /** Returns current chooser index. */
597 QModelIndex currentChooserIndex() const;
598
599 /** Returns chooser ID for @a iRow. */
[74878]600 QUuid chooserID(int iRow) const;
[51931]601 /** Returns current chooser ID. */
[74878]602 QUuid currentChooserID() const;
[51931]603
604 /** Returns chooser Name for @a iRow. */
605 QString chooserName(int iRow) const;
606 /** Returns current Name. */
607 QString currentChooserName() const;
608
609 /** Adds chooser item. */
[74942]610 void addChooserItem(const QUuid &uID,
[51931]611 const QString &strName,
612 const QString &strOsTypeID,
613 const int iPosition = -1);
614 /** Adds chooser item by machine. */
615 void addChooserItemByMachine(const CMachine &machine,
616 const int iPosition = -1);
617 /** Adds chooser item by ID. */
[74942]618 void addChooserItemByID(const QUuid &uID,
[51931]619 const int iPosition = -1);
620
621 /** Make sure chooser have current-index if possible. */
622 void makeSureChooserHaveCurrentIndexIfPossible();
623 /** @} */
624
625 /** @name Data-pane
626 * @{ */
627 /** Returns data index for @a iRow and @a iColumn. */
628 QModelIndex dataIndex(int iRow, int iColumn) const;
629
630 /** Returns data-key index for @a iRow. */
631 QModelIndex dataKeyIndex(int iRow) const;
632
633 /** Returns data-value index for @a iRow. */
634 QModelIndex dataValueIndex(int iRow) const;
635
636 /** Returns current data-key. */
637 QString dataKey(int iRow) const;
638
639 /** Returns current data-value. */
640 QString dataValue(int iRow) const;
641
642 /** Adds data item. */
643 void addDataItem(const QString &strKey,
644 const QString &strValue,
645 const int iPosition = -1);
646
647 /** Sorts data items. */
648 void sortData();
[51951]649
650 /** Returns the list of known extra-data keys. */
651 static QStringList knownExtraDataKeys();
[51931]652 /** @} */
653
654
[81357]655 /** @name Arguments
656 * @{ */
657 /** Holds the center widget reference. */
658 QWidget *m_pCenterWidget;
659 /** @} */
660
[51931]661 /** @name General
662 * @{ */
663 QVBoxLayout *m_pMainLayout;
664 /** Data pane: Tool-bar. */
[86233]665 QIToolBar *m_pToolBar;
[51931]666 /** Splitter. */
667 QISplitter *m_pSplitter;
668 /** @} */
669
670 /** @name Chooser-pane
671 * @{ */
672 /** Chooser pane. */
673 QWidget *m_pPaneOfChooser;
674 /** Chooser filter. */
675 QLineEdit *m_pFilterOfChooser;
676 /** Chooser pane: List-view. */
677 QListView *m_pViewOfChooser;
678 /** Chooser pane: Source-model. */
679 QStandardItemModel *m_pModelSourceOfChooser;
680 /** Chooser pane: Proxy-model. */
681 UIChooserPaneSortingModel *m_pModelProxyOfChooser;
682 /** @} */
683
684 /** @name Data-pane
685 * @{ */
686 /** Data pane. */
687 QWidget *m_pPaneOfData;
[55454]688 /** Data filter. */
[51931]689 QLineEdit *m_pFilterOfData;
690 /** Data pane: Table-view. */
691 QTableView *m_pViewOfData;
692 /** Data pane: Item-model. */
693 QStandardItemModel *m_pModelSourceOfData;
694 /** Data pane: Proxy-model. */
695 QSortFilterProxyModel *m_pModelProxyOfData;
696 /** @} */
697
698 /** @name Button Box
699 * @{ */
700 /** Dialog button-box. */
701 QIDialogButtonBox *m_pButtonBox;
702 /** @} */
703
704 /** @name Actions
705 * @{ */
706 /** Add action. */
707 QAction *m_pActionAdd;
708 /** Del action. */
709 QAction *m_pActionDel;
710 /** Load action. */
711 QAction *m_pActionLoad;
712 /** Save action. */
713 QAction *m_pActionSave;
714 /** @} */
715};
716
[71437]717
718/*********************************************************************************************************************************
719* Class UIExtraDataManagerWindow implementation. *
720*********************************************************************************************************************************/
721
[81357]722UIExtraDataManagerWindow::UIExtraDataManagerWindow(QWidget *pCenterWidget)
723 : m_pCenterWidget(pCenterWidget)
724 , m_pMainLayout(0), m_pToolBar(0), m_pSplitter(0)
[51931]725 , m_pPaneOfChooser(0), m_pFilterOfChooser(0), m_pViewOfChooser(0)
726 , m_pModelSourceOfChooser(0), m_pModelProxyOfChooser(0)
727 , m_pPaneOfData(0), m_pFilterOfData(0), m_pViewOfData(0),
728 m_pModelSourceOfData(0), m_pModelProxyOfData(0)
729 , m_pButtonBox(0)
730 , m_pActionAdd(0), m_pActionDel(0)
731 , m_pActionLoad(0), m_pActionSave(0)
732{
733 /* Prepare: */
734 prepare();
735}
736
737UIExtraDataManagerWindow::~UIExtraDataManagerWindow()
738{
739 /* Cleanup: */
740 cleanup();
741}
742
[51932]743void UIExtraDataManagerWindow::showAndRaise(QWidget*)
[51931]744{
745 /* Show: */
746 show();
747 /* Restore from minimized state: */
748 setWindowState(windowState() & ~Qt::WindowMinimized);
749 /* Raise: */
750 activateWindow();
751// /* Center according passed widget: */
[97682]752// gpDesktop->centerWidget(this, pCenterWidget, false);
[51931]753}
754
[74942]755void UIExtraDataManagerWindow::sltMachineRegistered(const QUuid &uID, bool fRegistered)
[51931]756{
757 /* Machine registered: */
758 if (fRegistered)
759 {
760 /* Gather list of 'known IDs': */
[74878]761 QList<QUuid> knownIDs;
[51931]762 for (int iRow = 0; iRow < m_pModelSourceOfChooser->rowCount(); ++iRow)
[74878]763 knownIDs.append(chooserID(iRow));
[51931]764
765 /* Get machine items: */
[79365]766 const CMachineVector machines = uiCommon().virtualBox().GetMachines();
[51931]767 /* Look for the proper place to insert new machine item: */
[74878]768 QUuid uPositionID = UIExtraDataManager::GlobalID;
[51931]769 foreach (const CMachine &machine, machines)
770 {
771 /* Get iterated machine ID: */
[74878]772 const QUuid uIteratedID = machine.GetId();
[51931]773 /* If 'iterated ID' equal to 'added ID' => break now: */
[74942]774 if (uIteratedID == uID)
[51931]775 break;
776 /* If 'iterated ID' is 'known ID' => remember it: */
[74878]777 if (knownIDs.contains(uIteratedID))
778 uPositionID = uIteratedID;
[51931]779 }
780
781 /* Add new chooser item into source-model: */
[74942]782 addChooserItemByID(uID, knownIDs.indexOf(uPositionID) + 1);
[51931]783 /* And sort proxy-model: */
784 m_pModelProxyOfChooser->sort(0, Qt::AscendingOrder);
785 /* Make sure chooser have current-index if possible: */
786 makeSureChooserHaveCurrentIndexIfPossible();
787 }
788 /* Machine unregistered: */
789 else
790 {
791 /* Remove chooser item with 'removed ID' if it is among 'known IDs': */
792 for (int iRow = 0; iRow < m_pModelSourceOfChooser->rowCount(); ++iRow)
[74942]793 if (chooserID(iRow) == uID)
[51931]794 m_pModelSourceOfChooser->removeRow(iRow);
795 }
796}
797
[74942]798void UIExtraDataManagerWindow::sltExtraDataMapAcknowledging(const QUuid &uID)
[51953]799{
800 /* Update item with 'changed ID' if it is among 'known IDs': */
801 for (int iRow = 0; iRow < m_pModelSourceOfChooser->rowCount(); ++iRow)
[74942]802 if (chooserID(iRow) == uID)
[51953]803 m_pModelSourceOfChooser->itemFromIndex(chooserIndex(iRow))->setData(true, Field_Known);
804}
805
[74942]806void UIExtraDataManagerWindow::sltExtraDataChange(const QUuid &uID, const QString &strKey, const QString &strValue)
[51931]807{
808 /* Skip unrelated IDs: */
[74942]809 if (currentChooserID() != uID)
[51931]810 return;
811
812 /* List of 'known keys': */
813 QStringList knownKeys;
814 for (int iRow = 0; iRow < m_pModelSourceOfData->rowCount(); ++iRow)
815 knownKeys << dataKey(iRow);
816
817 /* Check if 'changed key' is 'known key': */
818 int iPosition = knownKeys.indexOf(strKey);
819 /* If that is 'known key': */
820 if (iPosition != -1)
821 {
822 /* If 'changed value' is empty => REMOVE item: */
823 if (strValue.isEmpty())
824 m_pModelSourceOfData->removeRow(iPosition);
825 /* If 'changed value' is NOT empty => UPDATE item: */
826 else
[58377]827 {
828 m_pModelSourceOfData->itemFromIndex(dataKeyIndex(iPosition))->setData(strKey, Qt::UserRole);
[51931]829 m_pModelSourceOfData->itemFromIndex(dataValueIndex(iPosition))->setText(strValue);
[58377]830 }
[51931]831 }
832 /* Else if 'changed value' is NOT empty: */
833 else if (!strValue.isEmpty())
834 {
835 /* Look for the proper place for 'changed key': */
836 QString strPositionKey;
[74942]837 foreach (const QString &strIteratedKey, gEDataManager->map(uID).keys())
[51931]838 {
839 /* If 'iterated key' equal to 'changed key' => break now: */
840 if (strIteratedKey == strKey)
841 break;
842 /* If 'iterated key' is 'known key' => remember it: */
843 if (knownKeys.contains(strIteratedKey))
844 strPositionKey = strIteratedKey;
845 }
846 /* Calculate resulting position: */
847 iPosition = knownKeys.indexOf(strPositionKey) + 1;
848 /* INSERT item to the required position: */
849 addDataItem(strKey, strValue, iPosition);
850 /* And sort proxy-model: */
851 sortData();
852 }
853}
854
855void UIExtraDataManagerWindow::sltChooserApplyFilter(const QString &strFilter)
856{
857 /* Apply filtering rule: */
858 m_pModelProxyOfChooser->setFilterWildcard(strFilter);
859 /* Make sure chooser have current-index if possible: */
860 makeSureChooserHaveCurrentIndexIfPossible();
861}
862
863void UIExtraDataManagerWindow::sltChooserHandleCurrentChanged(const QModelIndex &index)
864{
865 /* Remove all the old items first: */
866 while (m_pModelSourceOfData->rowCount())
867 m_pModelSourceOfData->removeRow(0);
868
869 /* Ignore invalid indexes: */
870 if (!index.isValid())
871 return;
872
873 /* Add all the new items finally: */
[74878]874 const QUuid uID = index.data(Field_ID).toUuid();
875 if (!gEDataManager->contains(uID))
876 gEDataManager->hotloadMachineExtraDataMap(uID);
877 const ExtraDataMap data = gEDataManager->map(uID);
[51931]878 foreach (const QString &strKey, data.keys())
879 addDataItem(strKey, data.value(strKey));
880 /* And sort proxy-model: */
881 sortData();
882}
883
[51932]884void UIExtraDataManagerWindow::sltChooserHandleSelectionChanged(const QItemSelection&,
885 const QItemSelection&)
[51931]886{
887 /* Update actions availability: */
888 updateActionsAvailability();
889}
890
891void UIExtraDataManagerWindow::sltDataApplyFilter(const QString &strFilter)
892{
893 /* Apply filtering rule: */
894 m_pModelProxyOfData->setFilterWildcard(strFilter);
895}
896
[51932]897void UIExtraDataManagerWindow::sltDataHandleSelectionChanged(const QItemSelection&,
898 const QItemSelection&)
[51931]899{
900 /* Update actions availability: */
901 updateActionsAvailability();
902}
903
904void UIExtraDataManagerWindow::sltDataHandleItemChanged(QStandardItem *pItem)
905{
[58377]906 /* Make sure passed item is valid: */
907 AssertPtrReturnVoid(pItem);
[51931]908
[58377]909 /* Item-data index: */
910 const QModelIndex itemIndex = m_pModelSourceOfData->indexFromItem(pItem);
911 const int iRow = itemIndex.row();
912 const int iColumn = itemIndex.column();
[51931]913
[58377]914 /* Key-data is changed: */
915 if (iColumn == 0)
916 {
917 /* Should we replace changed key? */
918 bool fReplace = true;
919
920 /* List of 'known keys': */
921 QStringList knownKeys;
922 for (int iKeyRow = 0; iKeyRow < m_pModelSourceOfData->rowCount(); ++iKeyRow)
923 {
924 /* Do not consider the row we are changing as Qt's model is not yet updated: */
925 if (iKeyRow != iRow)
926 knownKeys << dataKey(iKeyRow);
927 }
928
929 /* If changed key exists: */
930 if (knownKeys.contains(itemIndex.data().toString()))
931 {
932 /* Show warning and ask for overwriting approval: */
933 if (!msgCenter().questionBinary(this, MessageType_Question,
934 QString("Overwriting already existing key, Continue?"),
935 0 /* auto-confirm id */,
936 QString("Overwrite") /* ok button text */,
937 QString() /* cancel button text */,
938 false /* ok button by default? */))
939 {
940 /* Cancel the operation, restore the original extra-data key: */
941 pItem->setData(itemIndex.data(Qt::UserRole).toString(), Qt::DisplayRole);
942 fReplace = false;
943 }
944 else
945 {
946 /* Delete previous extra-data key: */
947 gEDataManager->setExtraDataString(itemIndex.data().toString(),
948 QString(),
949 currentChooserID());
950 }
951 }
952
953 /* Replace changed extra-data key if necessary: */
954 if (fReplace)
955 {
956 gEDataManager->setExtraDataString(itemIndex.data(Qt::UserRole).toString(),
957 QString(),
958 currentChooserID());
959 gEDataManager->setExtraDataString(itemIndex.data().toString(),
960 dataValue(iRow),
961 currentChooserID());
962 }
963 }
964 /* Value-data is changed: */
965 else
966 {
967 /* Key-data index: */
968 const QModelIndex keyIndex = dataKeyIndex(iRow);
969 /* Update extra-data: */
970 gEDataManager->setExtraDataString(keyIndex.data().toString(),
971 itemIndex.data().toString(),
972 currentChooserID());
973 }
[51931]974}
975
976void UIExtraDataManagerWindow::sltDataHandleCustomContextMenuRequested(const QPoint &pos)
977{
978 /* Prepare menu: */
979 QMenu menu;
[55273]980 menu.addAction(m_pActionAdd);
[51931]981 menu.addAction(m_pActionDel);
[55273]982 menu.addSeparator();
[51931]983 menu.addAction(m_pActionSave);
984 /* Execute menu: */
985 m_pActionSave->setProperty("CalledFromContextMenu", true);
986 menu.exec(m_pViewOfData->viewport()->mapToGlobal(pos));
987 m_pActionSave->setProperty("CalledFromContextMenu", QVariant());
988}
989
990void UIExtraDataManagerWindow::sltAdd()
991{
992 /* Make sure this slot called by corresponding action only: */
993 QAction *pSenderAction = qobject_cast<QAction*>(sender());
994 AssertReturnVoid(pSenderAction && m_pActionAdd);
995
996 /* Create input-dialog: */
997 QPointer<QIDialog> pInputDialog = new QIDialog(this);
998 AssertPtrReturnVoid(pInputDialog.data());
999 {
1000 /* Configure input-dialog: */
1001 pInputDialog->setWindowTitle("Add extra-data record..");
1002 pInputDialog->setMinimumWidth(400);
1003 /* Create main-layout: */
1004 QVBoxLayout *pMainLayout = new QVBoxLayout(pInputDialog);
1005 AssertPtrReturnVoid(pMainLayout);
1006 {
1007 /* Create dialog validator group: */
1008 QObjectValidatorGroup *pValidatorGroup = new QObjectValidatorGroup(pInputDialog);
1009 AssertReturnVoid(pValidatorGroup);
1010 /* Create input-layout: */
1011 QGridLayout *pInputLayout = new QGridLayout;
1012 AssertPtrReturnVoid(pInputLayout);
1013 {
1014 /* Create key-label: */
1015 QLabel *pLabelKey = new QLabel("&Name:");
1016 {
1017 /* Configure key-label: */
1018 pLabelKey->setAlignment(Qt::AlignRight);
1019 /* Add key-label into input-layout: */
1020 pInputLayout->addWidget(pLabelKey, 0, 0);
1021 }
1022 /* Create key-editor: */
[51951]1023 QComboBox *pEditorKey = new QComboBox;
[51931]1024 {
1025 /* Configure key-editor: */
[51951]1026 pEditorKey->setEditable(true);
1027 pEditorKey->addItems(knownExtraDataKeys());
[51931]1028 pLabelKey->setBuddy(pEditorKey);
1029 /* Create key-editor property setter: */
1030 QObjectPropertySetter *pKeyPropertySetter = new QObjectPropertySetter(pInputDialog, "Key");
1031 AssertPtrReturnVoid(pKeyPropertySetter);
1032 {
1033 /* Configure key-editor property setter: */
[68348]1034 connect(pEditorKey, &QComboBox::editTextChanged,
1035 pKeyPropertySetter, &QObjectPropertySetter::sltAssignProperty);
[51931]1036 }
1037 /* Create key-editor validator: */
[93996]1038 QObjectValidator *pKeyValidator
1039 = new QObjectValidator(new QRegularExpressionValidator(QRegularExpression("[\\s\\S]+"), this));
[51931]1040 AssertPtrReturnVoid(pKeyValidator);
1041 {
1042 /* Configure key-editor validator: */
[68348]1043 connect(pEditorKey, &QComboBox::editTextChanged,
1044 pKeyValidator, &QObjectValidator::sltValidate);
[51931]1045 /* Add key-editor validator into dialog validator group: */
1046 pValidatorGroup->addObjectValidator(pKeyValidator);
1047 }
1048 /* Add key-editor into input-layout: */
1049 pInputLayout->addWidget(pEditorKey, 0, 1);
1050 }
1051 /* Create value-label: */
1052 QLabel *pLabelValue = new QLabel("&Value:");
1053 {
1054 /* Configure value-label: */
1055 pLabelValue->setAlignment(Qt::AlignRight);
1056 /* Add value-label into input-layout: */
1057 pInputLayout->addWidget(pLabelValue, 1, 0);
1058 }
1059 /* Create value-editor: */
1060 QLineEdit *pEditorValue = new QLineEdit;
1061 {
1062 /* Configure value-editor: */
1063 pLabelValue->setBuddy(pEditorValue);
1064 /* Create value-editor property setter: */
1065 QObjectPropertySetter *pValuePropertySetter = new QObjectPropertySetter(pInputDialog, "Value");
1066 AssertPtrReturnVoid(pValuePropertySetter);
1067 {
1068 /* Configure value-editor property setter: */
[68348]1069 connect(pEditorValue, &QLineEdit::textEdited,
1070 pValuePropertySetter, &QObjectPropertySetter::sltAssignProperty);
[51931]1071 }
1072 /* Create value-editor validator: */
[93996]1073 QObjectValidator *pValueValidator
1074 = new QObjectValidator(new QRegularExpressionValidator(QRegularExpression("[\\s\\S]+"), this));
[51931]1075 AssertPtrReturnVoid(pValueValidator);
1076 {
1077 /* Configure value-editor validator: */
[68348]1078 connect(pEditorValue, &QLineEdit::textEdited,
1079 pValueValidator, &QObjectValidator::sltValidate);
[51931]1080 /* Add value-editor validator into dialog validator group: */
1081 pValidatorGroup->addObjectValidator(pValueValidator);
1082 }
1083 /* Add value-editor into input-layout: */
1084 pInputLayout->addWidget(pEditorValue, 1, 1);
1085 }
1086 /* Add input-layout into main-layout: */
1087 pMainLayout->addLayout(pInputLayout);
1088 }
1089 /* Create stretch: */
1090 pMainLayout->addStretch();
1091 /* Create dialog button-box: */
1092 QIDialogButtonBox *pButtonBox = new QIDialogButtonBox;
1093 AssertPtrReturnVoid(pButtonBox);
1094 {
1095 /* Configure button-box: */
1096 pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
[51950]1097 pButtonBox->button(QDialogButtonBox::Ok)->setAutoDefault(true);
[51931]1098 pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(pValidatorGroup->result());
1099 pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
[68348]1100 connect(pValidatorGroup, &QObjectValidatorGroup::sigValidityChange,
1101 pButtonBox->button(QDialogButtonBox::Ok), &QPushButton::setEnabled);
[68351]1102 connect(pButtonBox, &QIDialogButtonBox::accepted, pInputDialog.data(), &QIDialog::accept);
1103 connect(pButtonBox, &QIDialogButtonBox::rejected, pInputDialog.data(), &QIDialog::reject);
[51931]1104 /* Add button-box into main-layout: */
1105 pMainLayout->addWidget(pButtonBox);
1106 }
1107 }
1108 }
1109
1110 /* Execute input-dialog: */
1111 if (pInputDialog->exec() == QDialog::Accepted)
1112 {
[58377]1113 /* Should we add new key? */
1114 bool fAdd = true;
1115
1116 /* List of 'known keys': */
1117 QStringList knownKeys;
1118 for (int iKeyRow = 0; iKeyRow < m_pModelSourceOfData->rowCount(); ++iKeyRow)
1119 knownKeys << dataKey(iKeyRow);
1120
1121 /* If new key exists: */
1122 if (knownKeys.contains(pInputDialog->property("Key").toString()))
1123 {
1124 /* Show warning and ask for overwriting approval: */
1125 if (!msgCenter().questionBinary(this, MessageType_Question,
1126 QString("Overwriting already existing key, Continue?"),
1127 0 /* auto-confirm id */,
1128 QString("Overwrite") /* ok button text */,
1129 QString() /* cancel button text */,
1130 false /* ok button by default? */))
1131 {
1132 /* Cancel the operation: */
1133 fAdd = false;
1134 }
1135 }
1136
1137 /* Add new extra-data key if necessary: */
1138 if (fAdd)
1139 gEDataManager->setExtraDataString(pInputDialog->property("Key").toString(),
1140 pInputDialog->property("Value").toString(),
1141 currentChooserID());
[51931]1142 }
1143
1144 /* Destroy input-dialog: */
1145 if (pInputDialog)
1146 delete pInputDialog;
1147}
1148
1149void UIExtraDataManagerWindow::sltDel()
1150{
1151 /* Make sure this slot called by corresponding action only: */
1152 QAction *pSenderAction = qobject_cast<QAction*>(sender());
1153 AssertReturnVoid(pSenderAction && m_pActionDel);
1154
1155 /* Gather the map of chosen items: */
1156 QMap<QString, QString> items;
1157 foreach (const QModelIndex &keyIndex, m_pViewOfData->selectionModel()->selectedRows(0))
1158 items.insert(keyIndex.data().toString(), dataValueIndex(keyIndex.row()).data().toString());
1159
1160 /* Prepare details: */
1161 const QString strTableTemplate("<!--EOM--><table border=0 cellspacing=10 cellpadding=0 width=500>%1</table>");
1162 const QString strRowTemplate("<tr><td><tt>%1</tt></td><td align=right><tt>%2</tt></td></tr>");
1163 QString strDetails;
1164 foreach (const QString &strKey, items.keys())
1165 strDetails += strRowTemplate.arg(strKey, items.value(strKey));
1166 strDetails = strTableTemplate.arg(strDetails);
1167
1168 /* Ask for user' confirmation: */
1169 if (!msgCenter().errorWithQuestion(this, MessageType_Question,
1170 QString("<p>Do you really wish to "
1171 "remove chosen records?</p>"),
[89950]1172 strDetails))
[51931]1173 return;
1174
1175 /* Erase all the chosen extra-data records: */
1176 foreach (const QString &strKey, items.keys())
1177 gEDataManager->setExtraDataString(strKey, QString(), currentChooserID());
1178}
1179
1180void UIExtraDataManagerWindow::sltSave()
1181{
1182 /* Make sure this slot called by corresponding action only: */
1183 QAction *pSenderAction = qobject_cast<QAction*>(sender());
1184 AssertReturnVoid(pSenderAction && m_pActionSave);
1185
1186 /* Compose initial file-name: */
[79365]1187 const QString strInitialFileName = QDir(uiCommon().homeFolder()).absoluteFilePath(QString("%1_ExtraData.xml").arg(currentChooserName()));
[51931]1188 /* Open file-save dialog to choose file to save extra-data into: */
1189 const QString strFileName = QIFileDialog::getSaveFileName(strInitialFileName, "XML files (*.xml)", this,
1190 "Choose file to save extra-data into..", 0, true, true);
1191 /* Make sure file-name was chosen: */
1192 if (strFileName.isEmpty())
1193 return;
1194
1195 /* Create file: */
1196 QFile output(strFileName);
1197 /* Open file for writing: */
1198 bool fOpened = output.open(QIODevice::WriteOnly);
1199 AssertReturnVoid(fOpened);
1200 {
1201 /* Create XML stream writer: */
1202 QXmlStreamWriter stream(&output);
1203 /* Configure XML stream writer: */
1204 stream.setAutoFormatting(true);
1205 stream.setAutoFormattingIndent(2);
1206 /* Write document: */
1207 stream.writeStartDocument();
1208 {
1209 stream.writeStartElement("VirtualBox");
1210 {
[74878]1211 const QUuid uID = currentChooserID();
1212 bool fIsMachine = uID != UIExtraDataManager::GlobalID;
[51931]1213 const QString strType = fIsMachine ? "Machine" : "Global";
1214 stream.writeStartElement(strType);
1215 {
1216 if (fIsMachine)
[74878]1217 stream.writeAttribute("uuid", QString("{%1}").arg(uID.toString()));
[51931]1218 stream.writeStartElement("ExtraData");
1219 {
1220 /* Called from context-menu: */
1221 if (pSenderAction->property("CalledFromContextMenu").toBool() &&
1222 !m_pViewOfData->selectionModel()->selection().isEmpty())
1223 {
1224 foreach (const QModelIndex &keyIndex, m_pViewOfData->selectionModel()->selectedRows())
1225 {
1226 /* Get data-value index: */
1227 const QModelIndex valueIndex = dataValueIndex(keyIndex.row());
1228 /* Write corresponding extra-data item into stream: */
1229 stream.writeStartElement("ExtraDataItem");
1230 {
1231 stream.writeAttribute("name", keyIndex.data().toString());
1232 stream.writeAttribute("value", valueIndex.data().toString());
1233 }
1234 stream.writeEndElement(); /* ExtraDataItem */
1235 }
1236 }
1237 /* Called from menu-bar/tool-bar: */
1238 else
1239 {
1240 for (int iRow = 0; iRow < m_pModelProxyOfData->rowCount(); ++iRow)
1241 {
1242 /* Get indexes: */
1243 const QModelIndex keyIndex = m_pModelProxyOfData->index(iRow, 0);
1244 const QModelIndex valueIndex = m_pModelProxyOfData->index(iRow, 1);
1245 /* Write corresponding extra-data item into stream: */
1246 stream.writeStartElement("ExtraDataItem");
1247 {
1248 stream.writeAttribute("name", keyIndex.data().toString());
1249 stream.writeAttribute("value", valueIndex.data().toString());
1250 }
1251 stream.writeEndElement(); /* ExtraDataItem */
1252 }
1253 }
1254 }
1255 stream.writeEndElement(); /* ExtraData */
1256 }
1257 stream.writeEndElement(); /* strType */
1258 }
1259 stream.writeEndElement(); /* VirtualBox */
1260 }
1261 stream.writeEndDocument();
1262 /* Close file: */
1263 output.close();
1264 }
1265}
1266
1267void UIExtraDataManagerWindow::sltLoad()
1268{
1269 /* Make sure this slot called by corresponding action only: */
1270 QAction *pSenderAction = qobject_cast<QAction*>(sender());
1271 AssertReturnVoid(pSenderAction && m_pActionLoad);
1272
1273 /* Compose initial file-name: */
[79365]1274 const QString strInitialFileName = QDir(uiCommon().homeFolder()).absoluteFilePath(QString("%1_ExtraData.xml").arg(currentChooserName()));
[51931]1275 /* Open file-open dialog to choose file to open extra-data into: */
1276 const QString strFileName = QIFileDialog::getOpenFileName(strInitialFileName, "XML files (*.xml)", this,
1277 "Choose file to load extra-data from..");
1278 /* Make sure file-name was chosen: */
1279 if (strFileName.isEmpty())
1280 return;
1281
1282 /* Create file: */
1283 QFile input(strFileName);
1284 /* Open file for writing: */
1285 bool fOpened = input.open(QIODevice::ReadOnly);
1286 AssertReturnVoid(fOpened);
1287 {
1288 /* Create XML stream reader: */
1289 QXmlStreamReader stream(&input);
1290 /* Read XML stream: */
1291 while (!stream.atEnd())
1292 {
1293 /* Read subsequent token: */
1294 const QXmlStreamReader::TokenType tokenType = stream.readNext();
1295 /* Skip non-interesting tokens: */
1296 if (tokenType != QXmlStreamReader::StartElement)
1297 continue;
1298
1299 /* Get the name of the current element: */
[97680]1300 const QString strElementName = stream.name().toString();
[51931]1301
1302 /* Search for the scope ID: */
[74878]1303 QUuid uLoadingID;
[97680]1304 if (strElementName == "Global")
[74878]1305 uLoadingID = UIExtraDataManager::GlobalID;
[97680]1306 else if (strElementName == "Machine")
[51931]1307 {
1308 const QXmlStreamAttributes attributes = stream.attributes();
1309 if (attributes.hasAttribute("uuid"))
1310 {
1311 const QString strUuid = attributes.value("uuid").toString();
[93983]1312 const QUuid uLoadingID(strUuid);
[74878]1313 if (uLoadingID.isNull())
[51931]1314 msgCenter().alert(this, MessageType_Warning,
1315 QString("<p>Invalid extra-data ID:</p>"
1316 "<p>%1</p>").arg(strUuid));
1317 }
1318 }
1319 /* Look particular extra-data entries: */
[97680]1320 else if (strElementName == "ExtraDataItem")
[51931]1321 {
1322 const QXmlStreamAttributes attributes = stream.attributes();
1323 if (attributes.hasAttribute("name") && attributes.hasAttribute("value"))
1324 {
1325 const QString strName = attributes.value("name").toString();
1326 const QString strValue = attributes.value("value").toString();
1327 gEDataManager->setExtraDataString(strName, strValue, currentChooserID());
1328 }
1329 }
1330
1331 /* Check extra-data ID: */
[74878]1332 if (!uLoadingID.isNull() && uLoadingID != currentChooserID() &&
[51931]1333 !msgCenter().questionBinary(this, MessageType_Question,
1334 QString("<p>Inconsistent extra-data ID:</p>"
1335 "<p>Current: {%1}</p>"
1336 "<p>Loading: {%2}</p>"
1337 "<p>Continue with loading?</p>")
[74878]1338 .arg(currentChooserID().toString(), uLoadingID.toString())))
[51931]1339 break;
1340 }
1341 /* Handle XML stream error: */
1342 if (stream.hasError())
1343 msgCenter().alert(this, MessageType_Warning,
1344 QString("<p>Error reading XML file:</p>"
1345 "<p>%1</p>").arg(stream.error()));
1346 /* Close file: */
1347 input.close();
1348 }
1349}
1350
[63291]1351bool UIExtraDataManagerWindow::shouldBeMaximized() const
1352{
1353 return gEDataManager->extraDataManagerShouldBeMaximized();
1354}
1355
[51931]1356void UIExtraDataManagerWindow::prepare()
1357{
1358 /* Prepare this: */
1359 prepareThis();
1360 /* Prepare connections: */
1361 prepareConnections();
1362 /* Prepare menu: */
1363 prepareMenu();
1364 /* Prepare central-widget: */
1365 prepareCentralWidget();
1366 /* Load settings: */
1367 loadSettings();
1368}
1369
1370void UIExtraDataManagerWindow::prepareThis()
1371{
[60362]1372#ifndef VBOX_WS_MAC
[100896]1373 /* Assign window icon: */
1374 setWindowIcon(UIIconPool::iconSetFull(":/edata_manager_32px.png", ":/edata_manager_16px.png"));
1375#endif
[51931]1376
1377 /* Apply window title: */
1378 setWindowTitle("Extra-data Manager");
1379
1380 /* Do not count that window as important for application,
1381 * it will NOT be taken into account when other top-level windows will be closed: */
1382 setAttribute(Qt::WA_QuitOnClose, false);
1383
1384 /* Delete window when closed: */
1385 setAttribute(Qt::WA_DeleteOnClose);
1386}
1387
1388void UIExtraDataManagerWindow::prepareConnections()
1389{
1390 /* Prepare connections: */
[68348]1391 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,
1392 this, &UIExtraDataManagerWindow::sltMachineRegistered);
[51931]1393}
1394
1395void UIExtraDataManagerWindow::prepareMenu()
1396{
1397 /* Create 'Actions' menu: */
1398 QMenu *pActionsMenu = menuBar()->addMenu("Actions");
1399 AssertReturnVoid(pActionsMenu);
1400 {
1401 /* Create 'Add' action: */
1402 m_pActionAdd = pActionsMenu->addAction("Add");
1403 AssertReturnVoid(m_pActionAdd);
1404 {
1405 /* Configure 'Add' action: */
[83367]1406 m_pActionAdd->setIcon(UIIconPool::iconSetFull(":/edata_add_24px.png", ":/edata_add_16px.png",
1407 ":/edata_add_disabled_24px.png", ":/edata_add_disabled_16px.png"));
[51950]1408 m_pActionAdd->setShortcut(QKeySequence("Ctrl+T"));
[68348]1409 connect(m_pActionAdd, &QAction::triggered, this, &UIExtraDataManagerWindow::sltAdd);
[51931]1410 }
1411 /* Create 'Del' action: */
1412 m_pActionDel = pActionsMenu->addAction("Remove");
1413 AssertReturnVoid(m_pActionDel);
1414 {
1415 /* Configure 'Del' action: */
[83367]1416 m_pActionDel->setIcon(UIIconPool::iconSetFull(":/edata_remove_24px.png", ":/edata_remove_16px.png",
1417 ":/edata_remove_disabled_24px.png", ":/edata_remove_disabled_16px.png"));
[51950]1418 m_pActionDel->setShortcut(QKeySequence("Ctrl+R"));
[68348]1419 connect(m_pActionDel, &QAction::triggered, this, &UIExtraDataManagerWindow::sltDel);
[51931]1420 }
1421
1422 /* Add separator: */
1423 pActionsMenu->addSeparator();
1424
1425 /* Create 'Load' action: */
1426 m_pActionLoad = pActionsMenu->addAction("Load");
1427 AssertReturnVoid(m_pActionLoad);
1428 {
1429 /* Configure 'Load' action: */
[83367]1430 m_pActionLoad->setIcon(UIIconPool::iconSetFull(":/edata_load_24px.png", ":/edata_load_16px.png",
1431 ":/edata_load_disabled_24px.png", ":/edata_load_disabled_16px.png"));
[51931]1432 m_pActionLoad->setShortcut(QKeySequence("Ctrl+L"));
[68348]1433 connect(m_pActionLoad, &QAction::triggered, this, &UIExtraDataManagerWindow::sltLoad);
[51931]1434 }
1435 /* Create 'Save' action: */
[55274]1436 m_pActionSave = pActionsMenu->addAction("Save As...");
[51931]1437 AssertReturnVoid(m_pActionSave);
1438 {
1439 /* Configure 'Save' action: */
[83367]1440 m_pActionSave->setIcon(UIIconPool::iconSetFull(":/edata_save_24px.png", ":/edata_save_16px.png",
1441 ":/edata_save_disabled_24px.png", ":/edata_save_disabled_16px.png"));
[51931]1442 m_pActionSave->setShortcut(QKeySequence("Ctrl+S"));
[68348]1443 connect(m_pActionSave, &QAction::triggered, this, &UIExtraDataManagerWindow::sltSave);
[51931]1444 }
1445 }
1446}
1447
1448void UIExtraDataManagerWindow::prepareCentralWidget()
1449{
1450 /* Prepare central-widget: */
1451 setCentralWidget(new QWidget);
1452 AssertPtrReturnVoid(centralWidget());
1453 {
1454 /* Prepare layout: */
1455 m_pMainLayout = new QVBoxLayout(centralWidget());
1456 AssertReturnVoid(m_pMainLayout && centralWidget()->layout() &&
1457 m_pMainLayout == centralWidget()->layout());
1458 {
[60362]1459#ifdef VBOX_WS_MAC
[51931]1460 /* No spacing/margins on the Mac: */
1461 m_pMainLayout->setContentsMargins(0, 0, 0, 0);
1462 m_pMainLayout->insertSpacing(0, 10);
[60362]1463#else /* !VBOX_WS_MAC */
[51931]1464 /* Set spacing/margin like in the selector window: */
[69726]1465 const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 2;
1466 const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) / 2;
1467 const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 2;
1468 const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) / 2;
1469 m_pMainLayout->setContentsMargins(iL, iT, iR, iB);
[60362]1470#endif /* !VBOX_WS_MAC */
[51931]1471 /* Prepare tool-bar: */
1472 prepareToolBar();
1473 /* Prepare splitter: */
1474 prepareSplitter();
1475 /* Prepare button-box: */
1476 prepareButtonBox();
1477 }
1478 /* Initial focus: */
[51937]1479 if (m_pViewOfChooser)
1480 m_pViewOfChooser->setFocus();
[51931]1481 }
1482}
1483
1484void UIExtraDataManagerWindow::prepareToolBar()
1485{
1486 /* Create tool-bar: */
[86233]1487 m_pToolBar = new QIToolBar(this);
[51931]1488 AssertPtrReturnVoid(m_pToolBar);
1489 {
1490 /* Configure tool-bar: */
[83367]1491 m_pToolBar->setIconSize(QSize(24, 24));
[51931]1492 m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
1493 /* Add actions: */
1494 m_pToolBar->addAction(m_pActionAdd);
1495 m_pToolBar->addAction(m_pActionDel);
1496 m_pToolBar->addSeparator();
1497 m_pToolBar->addAction(m_pActionLoad);
1498 m_pToolBar->addAction(m_pActionSave);
1499 /* Integrate tool-bar into dialog: */
[60362]1500#ifdef VBOX_WS_MAC
[51931]1501 /* Enable unified tool-bars on Mac OS X. Available on Qt >= 4.3: */
1502 addToolBar(m_pToolBar);
[51937]1503 m_pToolBar->enableMacToolbar();
[60362]1504#else /* !VBOX_WS_MAC */
[51931]1505 /* Add tool-bar into main-layout: */
1506 m_pMainLayout->addWidget(m_pToolBar);
[60362]1507#endif /* !VBOX_WS_MAC */
[51931]1508 }
1509}
1510
1511void UIExtraDataManagerWindow::prepareSplitter()
1512{
1513 /* Create splitter: */
1514 m_pSplitter = new QISplitter;
1515 AssertPtrReturnVoid(m_pSplitter);
1516 {
1517 /* Prepare panes: */
1518 preparePanes();
1519 /* Configure splitter: */
1520 m_pSplitter->setChildrenCollapsible(false);
1521 m_pSplitter->setStretchFactor(0, 0);
1522 m_pSplitter->setStretchFactor(1, 1);
1523 /* Add splitter into main layout: */
1524 m_pMainLayout->addWidget(m_pSplitter);
1525 }
1526}
1527
1528void UIExtraDataManagerWindow::preparePanes()
1529{
1530 /* Prepare chooser-pane: */
1531 preparePaneChooser();
1532 /* Prepare data-pane: */
1533 preparePaneData();
1534 /* Link chooser and data panes: */
[68348]1535 connect(m_pViewOfChooser->selectionModel(), &QItemSelectionModel::currentChanged,
1536 this, &UIExtraDataManagerWindow::sltChooserHandleCurrentChanged);
1537 connect(m_pViewOfChooser->selectionModel(), &QItemSelectionModel::selectionChanged,
1538 this, &UIExtraDataManagerWindow::sltChooserHandleSelectionChanged);
1539 connect(m_pViewOfData->selectionModel(), &QItemSelectionModel::selectionChanged,
1540 this, &UIExtraDataManagerWindow::sltDataHandleSelectionChanged);
1541 connect(m_pModelSourceOfData, &QStandardItemModel::itemChanged,
1542 this, &UIExtraDataManagerWindow::sltDataHandleItemChanged);
[51931]1543 /* Make sure chooser have current-index if possible: */
1544 makeSureChooserHaveCurrentIndexIfPossible();
1545}
1546
1547void UIExtraDataManagerWindow::preparePaneChooser()
1548{
1549 /* Create chooser-pane: */
1550 m_pPaneOfChooser = new QWidget;
1551 AssertPtrReturnVoid(m_pPaneOfChooser);
1552 {
1553 /* Create layout: */
1554 QVBoxLayout *pLayout = new QVBoxLayout(m_pPaneOfChooser);
1555 AssertReturnVoid(pLayout && m_pPaneOfChooser->layout() &&
1556 pLayout == m_pPaneOfChooser->layout());
1557 {
1558 /* Configure layout: */
[69726]1559 const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 3;
1560 pLayout->setContentsMargins(0, 0, iR, 0);
[51931]1561 /* Create chooser-filter: */
1562 m_pFilterOfChooser = new QLineEdit;
1563 {
1564 /* Configure chooser-filter: */
1565 m_pFilterOfChooser->setPlaceholderText("Search..");
[68348]1566 connect(m_pFilterOfChooser, &QLineEdit::textChanged,
1567 this, &UIExtraDataManagerWindow::sltChooserApplyFilter);
[51931]1568 /* Add chooser-filter into layout: */
1569 pLayout->addWidget(m_pFilterOfChooser);
1570 }
1571 /* Create chooser-view: */
1572 m_pViewOfChooser = new QListView;
1573 AssertPtrReturnVoid(m_pViewOfChooser);
1574 {
1575 /* Configure chooser-view: */
1576 delete m_pViewOfChooser->itemDelegate();
1577 m_pViewOfChooser->setItemDelegate(new UIChooserPaneDelegate(m_pViewOfChooser));
1578 m_pViewOfChooser->setSelectionMode(QAbstractItemView::SingleSelection);
1579 /* Create source-model: */
1580 m_pModelSourceOfChooser = new QStandardItemModel(m_pViewOfChooser);
1581 AssertPtrReturnVoid(m_pModelSourceOfChooser);
1582 {
1583 /* Create proxy-model: */
1584 m_pModelProxyOfChooser = new UIChooserPaneSortingModel(m_pViewOfChooser);
1585 AssertPtrReturnVoid(m_pModelProxyOfChooser);
1586 {
1587 /* Configure proxy-model: */
1588 m_pModelProxyOfChooser->setSortRole(Field_Name);
1589 m_pModelProxyOfChooser->setFilterRole(Field_Name);
1590 m_pModelProxyOfChooser->setSortCaseSensitivity(Qt::CaseInsensitive);
1591 m_pModelProxyOfChooser->setFilterCaseSensitivity(Qt::CaseInsensitive);
1592 m_pModelProxyOfChooser->setSourceModel(m_pModelSourceOfChooser);
1593 m_pViewOfChooser->setModel(m_pModelProxyOfChooser);
1594 }
1595 /* Add global chooser item into source-model: */
1596 addChooserItemByID(UIExtraDataManager::GlobalID);
1597 /* Add machine chooser items into source-model: */
[79365]1598 CMachineVector machines = uiCommon().virtualBox().GetMachines();
[51931]1599 foreach (const CMachine &machine, machines)
1600 addChooserItemByMachine(machine);
1601 /* And sort proxy-model: */
1602 m_pModelProxyOfChooser->sort(0, Qt::AscendingOrder);
1603 }
1604 /* Add chooser-view into layout: */
1605 pLayout->addWidget(m_pViewOfChooser);
1606 }
1607 }
1608 /* Add chooser-pane into splitter: */
1609 m_pSplitter->addWidget(m_pPaneOfChooser);
1610 }
1611}
1612
1613void UIExtraDataManagerWindow::preparePaneData()
1614{
1615 /* Create data-pane: */
1616 m_pPaneOfData = new QWidget;
1617 AssertPtrReturnVoid(m_pPaneOfData);
1618 {
1619 /* Create layout: */
1620 QVBoxLayout *pLayout = new QVBoxLayout(m_pPaneOfData);
1621 AssertReturnVoid(pLayout && m_pPaneOfData->layout() &&
1622 pLayout == m_pPaneOfData->layout());
1623 {
1624 /* Configure layout: */
[69726]1625 const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 3;
1626 pLayout->setContentsMargins(iL, 0, 0, 0);
[51931]1627 /* Create data-filter: */
1628 m_pFilterOfData = new QLineEdit;
1629 {
1630 /* Configure data-filter: */
1631 m_pFilterOfData->setPlaceholderText("Search..");
[68348]1632 connect(m_pFilterOfData, &QLineEdit::textChanged,
1633 this, &UIExtraDataManagerWindow::sltDataApplyFilter);
[51931]1634 /* Add data-filter into layout: */
1635 pLayout->addWidget(m_pFilterOfData);
1636 }
1637 /* Create data-view: */
1638 m_pViewOfData = new QTableView;
1639 AssertPtrReturnVoid(m_pViewOfData);
1640 {
1641 /* Create item-model: */
1642 m_pModelSourceOfData = new QStandardItemModel(0, 2, m_pViewOfData);
1643 AssertPtrReturnVoid(m_pModelSourceOfData);
1644 {
1645 /* Create proxy-model: */
1646 m_pModelProxyOfData = new QSortFilterProxyModel(m_pViewOfChooser);
1647 AssertPtrReturnVoid(m_pModelProxyOfData);
1648 {
1649 /* Configure proxy-model: */
1650 m_pModelProxyOfData->setSortCaseSensitivity(Qt::CaseInsensitive);
1651 m_pModelProxyOfData->setFilterCaseSensitivity(Qt::CaseInsensitive);
1652 m_pModelProxyOfData->setSourceModel(m_pModelSourceOfData);
1653 m_pViewOfData->setModel(m_pModelProxyOfData);
1654 }
1655 /* Configure item-model: */
1656 m_pModelSourceOfData->setHorizontalHeaderLabels(QStringList() << "Key" << "Value");
1657 }
1658 /* Configure data-view: */
1659 m_pViewOfData->setSortingEnabled(true);
1660 m_pViewOfData->setAlternatingRowColors(true);
1661 m_pViewOfData->setContextMenuPolicy(Qt::CustomContextMenu);
1662 m_pViewOfData->setSelectionMode(QAbstractItemView::ExtendedSelection);
1663 m_pViewOfData->setSelectionBehavior(QAbstractItemView::SelectRows);
[68348]1664 connect(m_pViewOfData, &QTableView::customContextMenuRequested,
1665 this, &UIExtraDataManagerWindow::sltDataHandleCustomContextMenuRequested);
[51931]1666 QHeaderView *pVHeader = m_pViewOfData->verticalHeader();
1667 QHeaderView *pHHeader = m_pViewOfData->horizontalHeader();
1668 pVHeader->hide();
1669 pHHeader->setSortIndicator(0, Qt::AscendingOrder);
1670 pHHeader->resizeSection(0, qMin(300, pHHeader->width() / 3));
1671 pHHeader->setStretchLastSection(true);
1672 /* Add data-view into layout: */
1673 pLayout->addWidget(m_pViewOfData);
1674 }
1675 }
1676 /* Add data-pane into splitter: */
1677 m_pSplitter->addWidget(m_pPaneOfData);
1678 }
1679}
1680
1681void UIExtraDataManagerWindow::prepareButtonBox()
1682{
1683 /* Create button-box: */
1684 m_pButtonBox = new QIDialogButtonBox;
1685 AssertPtrReturnVoid(m_pButtonBox);
1686 {
1687 /* Configure button-box: */
1688 m_pButtonBox->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::Close);
1689 m_pButtonBox->button(QDialogButtonBox::Close)->setShortcut(Qt::Key_Escape);
[99946]1690 connect(m_pButtonBox, &QIDialogButtonBox::helpRequested, m_pButtonBox, &QIDialogButtonBox::sltHandleHelpRequest);
[68348]1691 connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIExtraDataManagerWindow::close);
[51931]1692 /* Add button-box into main layout: */
1693 m_pMainLayout->addWidget(m_pButtonBox);
1694 }
1695}
1696
1697void UIExtraDataManagerWindow::loadSettings()
1698{
1699 /* Load window geometry: */
1700 {
[81357]1701 const QRect geo = gEDataManager->extraDataManagerGeometry(this, m_pCenterWidget);
[63291]1702 LogRel2(("GUI: UIExtraDataManagerWindow: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
[81255]1703 geo.x(), geo.y(), geo.width(), geo.height()));
1704 restoreGeometry(geo);
[51931]1705 }
1706
1707 /* Load splitter hints: */
1708 {
1709 m_pSplitter->setSizes(gEDataManager->extraDataManagerSplitterHints(this));
1710 }
1711}
1712
1713void UIExtraDataManagerWindow::saveSettings()
1714{
1715 /* Save splitter hints: */
1716 {
1717 gEDataManager->setExtraDataManagerSplitterHints(m_pSplitter->sizes());
1718 }
1719
1720 /* Save window geometry: */
1721 {
[81255]1722 const QRect geo = currentGeometry();
1723 LogRel2(("GUI: UIExtraDataManagerWindow: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
1724 geo.x(), geo.y(), geo.width(), geo.height()));
1725 gEDataManager->setExtraDataManagerGeometry(geo, isCurrentlyMaximized());
[51931]1726 }
1727}
1728
1729void UIExtraDataManagerWindow::cleanup()
1730{
1731 /* Save settings: */
1732 saveSettings();
1733}
1734
1735void UIExtraDataManagerWindow::updateActionsAvailability()
1736{
1737 /* Is there something selected in chooser-view? */
1738 bool fChooserHasSelection = !m_pViewOfChooser->selectionModel()->selection().isEmpty();
1739 /* Is there something selected in data-view? */
1740 bool fDataHasSelection = !m_pViewOfData->selectionModel()->selection().isEmpty();
1741
1742 /* Enable/disable corresponding actions: */
1743 m_pActionAdd->setEnabled(fChooserHasSelection);
1744 m_pActionDel->setEnabled(fChooserHasSelection && fDataHasSelection);
1745 m_pActionLoad->setEnabled(fChooserHasSelection);
1746 m_pActionSave->setEnabled(fChooserHasSelection);
1747}
1748
1749QModelIndex UIExtraDataManagerWindow::chooserIndex(int iRow) const
1750{
1751 return m_pModelSourceOfChooser->index(iRow, 0);
1752}
1753
1754QModelIndex UIExtraDataManagerWindow::currentChooserIndex() const
1755{
1756 return m_pViewOfChooser->currentIndex();
1757}
1758
[74878]1759QUuid UIExtraDataManagerWindow::chooserID(int iRow) const
[51931]1760{
[74878]1761 return chooserIndex(iRow).data(Field_ID).toUuid();
[51931]1762}
1763
[74878]1764QUuid UIExtraDataManagerWindow::currentChooserID() const
[51931]1765{
[74878]1766 return currentChooserIndex().data(Field_ID).toUuid();
[51931]1767}
1768
1769QString UIExtraDataManagerWindow::chooserName(int iRow) const
1770{
1771 return chooserIndex(iRow).data(Field_Name).toString();
1772}
1773
1774QString UIExtraDataManagerWindow::currentChooserName() const
1775{
1776 return currentChooserIndex().data(Field_Name).toString();
1777}
1778
[74942]1779void UIExtraDataManagerWindow::addChooserItem(const QUuid &uID,
[51931]1780 const QString &strName,
1781 const QString &strOsTypeID,
1782 const int iPosition /* = -1 */)
1783{
1784 /* Create item: */
1785 QStandardItem *pItem = new QStandardItem;
1786 AssertPtrReturnVoid(pItem);
1787 {
1788 /* Which is NOT editable: */
1789 pItem->setEditable(false);
1790 /* Contains passed ID: */
[74942]1791 pItem->setData(uID, Field_ID);
[51931]1792 /* Contains passed name: */
1793 pItem->setData(strName, Field_Name);
1794 /* Contains passed OS Type ID: */
1795 pItem->setData(strOsTypeID, Field_OsTypeID);
1796 /* And designated as known/unknown depending on extra-data manager status: */
[74942]1797 pItem->setData(gEDataManager->contains(uID), Field_Known);
[51931]1798 /* If insert position defined: */
1799 if (iPosition != -1)
1800 {
1801 /* Insert this item at specified position: */
1802 m_pModelSourceOfChooser->insertRow(iPosition, pItem);
1803 }
1804 /* If insert position undefined: */
1805 else
1806 {
1807 /* Add this item as the last one: */
1808 m_pModelSourceOfChooser->appendRow(pItem);
1809 }
1810 }
1811}
1812
1813void UIExtraDataManagerWindow::addChooserItemByMachine(const CMachine &machine,
1814 const int iPosition /* = -1 */)
1815{
[53311]1816 /* Make sure VM is accessible: */
1817 if (!machine.isNull() && machine.GetAccessible())
1818 return addChooserItem(machine.GetId(), machine.GetName(), machine.GetOSTypeId(), iPosition);
[51931]1819}
1820
[74942]1821void UIExtraDataManagerWindow::addChooserItemByID(const QUuid &uID,
[51931]1822 const int iPosition /* = -1 */)
1823{
1824 /* Global ID? */
[74942]1825 if (uID == UIExtraDataManager::GlobalID)
1826 return addChooserItem(uID, QString("Global"), QString(), iPosition);
[53311]1827
1828 /* Search for the corresponding machine by ID: */
[79365]1829 CVirtualBox vbox = uiCommon().virtualBox();
[74942]1830 const CMachine machine = vbox.FindMachine(uID.toString());
[53311]1831 /* Make sure VM is accessible: */
1832 if (vbox.isOk() && !machine.isNull() && machine.GetAccessible())
[74942]1833 return addChooserItem(uID, machine.GetName(), machine.GetOSTypeId(), iPosition);
[51931]1834}
1835
1836void UIExtraDataManagerWindow::makeSureChooserHaveCurrentIndexIfPossible()
1837{
1838 /* Make sure chooser have current-index if possible: */
1839 if (!m_pViewOfChooser->currentIndex().isValid())
1840 {
1841 /* Do we still have anything to select? */
1842 const QModelIndex firstIndex = m_pModelProxyOfChooser->index(0, 0);
1843 if (firstIndex.isValid())
1844 m_pViewOfChooser->setCurrentIndex(firstIndex);
1845 }
1846}
1847
1848QModelIndex UIExtraDataManagerWindow::dataIndex(int iRow, int iColumn) const
1849{
1850 return m_pModelSourceOfData->index(iRow, iColumn);
1851}
1852
1853QModelIndex UIExtraDataManagerWindow::dataKeyIndex(int iRow) const
1854{
1855 return dataIndex(iRow, 0);
1856}
1857
1858QModelIndex UIExtraDataManagerWindow::dataValueIndex(int iRow) const
1859{
1860 return dataIndex(iRow, 1);
1861}
1862
1863QString UIExtraDataManagerWindow::dataKey(int iRow) const
1864{
1865 return dataKeyIndex(iRow).data().toString();
1866}
1867
1868QString UIExtraDataManagerWindow::dataValue(int iRow) const
1869{
1870 return dataValueIndex(iRow).data().toString();
1871}
1872
1873void UIExtraDataManagerWindow::addDataItem(const QString &strKey,
1874 const QString &strValue,
1875 const int iPosition /* = -1 */)
1876{
1877 /* Prepare items: */
1878 QList<QStandardItem*> items;
1879 /* Create key item: */
1880 items << new QStandardItem(strKey);
[58377]1881 items.last()->setData(strKey, Qt::UserRole);
[51931]1882 AssertPtrReturnVoid(items.last());
1883 /* Create value item: */
1884 items << new QStandardItem(strValue);
1885 AssertPtrReturnVoid(items.last());
1886 /* If insert position defined: */
1887 if (iPosition != -1)
1888 {
1889 /* Insert these items as the row at the required position: */
1890 m_pModelSourceOfData->insertRow(iPosition, items);
1891 }
1892 /* If insert position undefined: */
1893 else
1894 {
1895 /* Add these items as the last one row: */
1896 m_pModelSourceOfData->appendRow(items);
1897 }
1898}
1899
1900void UIExtraDataManagerWindow::sortData()
1901{
1902 /* Sort using current rules: */
1903 const QHeaderView *pHHeader = m_pViewOfData->horizontalHeader();
1904 const int iSortSection = pHHeader->sortIndicatorSection();
1905 const Qt::SortOrder sortOrder = pHHeader->sortIndicatorOrder();
1906 m_pModelProxyOfData->sort(iSortSection, sortOrder);
1907}
[51951]1908
1909/* static */
1910QStringList UIExtraDataManagerWindow::knownExtraDataKeys()
1911{
1912 return QStringList()
1913 << QString()
[81612]1914 << GUI_RestrictedDialogs
[92398]1915 << GUI_SuppressMessages << GUI_InvertMessageOption
[99184]1916#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
[93408]1917 << GUI_NotificationCenter_KeepSuccessfullProgresses
[99184]1918#endif
[93408]1919 << GUI_NotificationCenter_Alignment
1920 << GUI_NotificationCenter_Order
[96808]1921 << GUI_PreventBetaLabel
[51951]1922#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
1923 << GUI_PreventApplicationUpdate << GUI_UpdateDate << GUI_UpdateCheckCount
[99184]1924#endif
[69006]1925 << GUI_Progress_LegacyMode
[85772]1926 << GUI_Customizations
[51951]1927 << GUI_RestrictedGlobalSettingsPages << GUI_RestrictedMachineSettingsPages
[66587]1928 << GUI_LanguageID
[53249]1929 << GUI_ActivateHoveredMachineWindow
[89688]1930 << GUI_DisableHostScreenSaver
[51951]1931 << GUI_Input_SelectorShortcuts << GUI_Input_MachineShortcuts
[77376]1932 << GUI_RecentFolderHD << GUI_RecentFolderCD << GUI_RecentFolderFD
[92492]1933 << GUI_VISOCreator_RecentFolder << GUI_VISOCreator_DialogGeometry
[51951]1934 << GUI_RecentListHD << GUI_RecentListCD << GUI_RecentListFD
[78519]1935 << GUI_RestrictedNetworkAttachmentTypes
[51951]1936 << GUI_LastSelectorWindowPosition << GUI_SplitterSizes
[68304]1937 << GUI_Toolbar << GUI_Toolbar_Text
1938 << GUI_Toolbar_MachineTools_Order << GUI_Toolbar_GlobalTools_Order
[74265]1939 << GUI_Tools_LastItemsSelected
[68304]1940 << GUI_Statusbar
[51951]1941 << GUI_GroupDefinitions << GUI_LastItemSelected
[75306]1942 << GUI_Details_Elements
1943 << GUI_Details_Elements_Preview_UpdateInterval
[74865]1944 << GUI_SnapshotManager_Details_Expanded
1945 << GUI_VirtualMediaManager_Details_Expanded
1946 << GUI_HostNetworkManager_Details_Expanded
[85035]1947 << GUI_CloudProfileManager_Restrictions
[74871]1948 << GUI_CloudProfileManager_Details_Expanded
[85389]1949 << GUI_CloudConsoleManager_Restrictions
1950 << GUI_CloudConsoleManager_Details_Expanded
[85454]1951 << GUI_CloudConsole_PublicKey_Path
[51951]1952 << GUI_HideDescriptionForWizards
1953 << GUI_HideFromManager << GUI_HideDetails
1954 << GUI_PreventReconfiguration << GUI_PreventSnapshotOperations
[60362]1955#ifndef VBOX_WS_MAC
[51951]1956 << GUI_MachineWindowIcons << GUI_MachineWindowNamePostfix
[99184]1957#endif
[51951]1958 << GUI_LastNormalWindowPosition << GUI_LastScaleWindowPosition
[67942]1959#ifndef VBOX_WS_MAC
1960 << GUI_MenuBar_Enabled
1961#endif
1962 << GUI_MenuBar_ContextMenu_Enabled
[51951]1963 << GUI_RestrictedRuntimeMenus
1964 << GUI_RestrictedRuntimeApplicationMenuActions
1965 << GUI_RestrictedRuntimeMachineMenuActions
1966 << GUI_RestrictedRuntimeViewMenuActions
[52937]1967 << GUI_RestrictedRuntimeInputMenuActions
[51951]1968 << GUI_RestrictedRuntimeDevicesMenuActions
1969#ifdef VBOX_WITH_DEBUGGER_GUI
1970 << GUI_RestrictedRuntimeDebuggerMenuActions
[99184]1971#endif
[60362]1972#ifdef VBOX_WS_MAC
[54272]1973 << GUI_RestrictedRuntimeWindowMenuActions
[99184]1974#endif
[51951]1975 << GUI_RestrictedRuntimeHelpMenuActions
1976 << GUI_RestrictedVisualStates
1977 << GUI_Fullscreen << GUI_Seamless << GUI_Scale
[100064]1978#ifdef VBOX_WS_NIX
[52788]1979 << GUI_Fullscreen_LegacyMode
[69722]1980 << GUI_DistinguishMachineWindowGroups
[99184]1981#endif
[56704]1982 << GUI_AutoresizeGuest << GUI_LastVisibilityStatusForGuestScreen << GUI_LastGuestSizeHint
[51951]1983 << GUI_VirtualScreenToHostScreen << GUI_AutomountGuestScreens
[60362]1984#ifndef VBOX_WS_MAC
[51951]1985 << GUI_ShowMiniToolBar << GUI_MiniToolBarAutoHide << GUI_MiniToolBarAlignment
[99184]1986#endif
[67942]1987 << GUI_StatusBar_Enabled << GUI_StatusBar_ContextMenu_Enabled << GUI_RestrictedStatusBarIndicators << GUI_StatusBar_IndicatorOrder
[60362]1988#ifdef VBOX_WS_MAC
[58674]1989 << GUI_RealtimeDockIconUpdateEnabled << GUI_RealtimeDockIconUpdateMonitor << GUI_DockIconDisableOverlay
[99184]1990#endif
[51951]1991 << GUI_PassCAD
[52190]1992 << GUI_MouseCapturePolicy
[51951]1993 << GUI_GuruMeditationHandler
1994 << GUI_HidLedsSync
[55187]1995 << GUI_ScaleFactor << GUI_Scaling_Optimization
[80773]1996 << GUI_SessionInformationDialogGeometry
[75057]1997 << GUI_GuestControl_ProcessControlSplitterHints
[75087]1998 << GUI_GuestControl_FileManagerDialogGeometry
[75642]1999 << GUI_GuestControl_FileManagerOptions
[75087]2000 << GUI_GuestControl_ProcessControlDialogGeometry
[51951]2001 << GUI_DefaultCloseAction << GUI_RestrictedCloseActions
[90083]2002 << GUI_LastCloseAction << GUI_CloseActionHook << GUI_DiscardStateOnPowerOff
[51951]2003#ifdef VBOX_WITH_DEBUGGER_GUI
2004 << GUI_Dbg_Enabled << GUI_Dbg_AutoShow
[99184]2005#endif
[57496]2006 << GUI_ExtraDataManager_Geometry << GUI_ExtraDataManager_SplitterHints
[86764]2007 << GUI_LogWindowGeometry
[92436]2008 << GUI_HelpBrowser_LastURLList
2009 << GUI_HelpBrowser_DialogGeometry
2010 << GUI_HelpBrowser_Bookmarks
2011 << GUI_HelpBrowser_ZoomPercentage;
[51951]2012}
[71437]2013
[60599]2014#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
[51931]2015
2016
[71437]2017/*********************************************************************************************************************************
2018* Class UIExtraDataManager implementation. *
2019*********************************************************************************************************************************/
2020
[51931]2021/* static */
[71580]2022UIExtraDataManager *UIExtraDataManager::s_pInstance = 0;
[74878]2023const QUuid UIExtraDataManager::GlobalID;
[30677]2024
2025/* static */
[51035]2026UIExtraDataManager* UIExtraDataManager::instance()
[30677]2027{
[51047]2028 /* Create/prepare instance if not yet exists: */
[71580]2029 if (!s_pInstance)
[51047]2030 {
2031 new UIExtraDataManager;
[71580]2032 s_pInstance->prepare();
[51047]2033 }
[51038]2034 /* Return instance: */
[71580]2035 return s_pInstance;
[30677]2036}
2037
2038/* static */
[51035]2039void UIExtraDataManager::destroy()
[30677]2040{
[51047]2041 /* Destroy/cleanup instance if still exists: */
[71580]2042 if (s_pInstance)
[30677]2043 {
[71580]2044 s_pInstance->cleanup();
2045 delete s_pInstance;
[30677]2046 }
2047}
2048
[60599]2049#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
[51931]2050/* static */
2051void UIExtraDataManager::openWindow(QWidget *pCenterWidget)
2052{
2053 /* Pass to instance: */
2054 instance()->open(pCenterWidget);
2055}
[60599]2056#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
[51931]2057
[74942]2058void UIExtraDataManager::hotloadMachineExtraDataMap(const QUuid &uID)
[51953]2059{
2060 /* Make sure it is valid ID: */
[74942]2061 AssertMsgReturnVoid(!uID.isNull() && uID != GlobalID,
2062 ("Invalid VM ID = {%s}\n", uID.toString().toUtf8().constData()));
[51953]2063 /* Which is not loaded yet: */
[74942]2064 AssertReturnVoid(!m_data.contains(uID));
[51953]2065
2066 /* Search for corresponding machine: */
[79365]2067 CVirtualBox vbox = uiCommon().virtualBox();
[74942]2068 CMachine machine = vbox.FindMachine(uID.toString());
[92027]2069 if (machine.isNull())
2070 return;
[51953]2071
2072 /* Make sure at least empty map is created: */
[74942]2073 m_data[uID] = ExtraDataMap();
[51953]2074
2075 /* Do not handle inaccessible machine: */
2076 if (!machine.GetAccessible())
2077 return;
2078
2079 /* Load machine extra-data map: */
2080 foreach (const QString &strKey, machine.GetExtraDataKeys())
[74942]2081 m_data[uID][strKey] = machine.GetExtraData(strKey);
[51953]2082
2083 /* Notifies about extra-data map acknowledged: */
[74942]2084 emit sigExtraDataMapAcknowledging(uID);
[51953]2085}
2086
[74942]2087QString UIExtraDataManager::extraDataString(const QString &strKey, const QUuid &uID /* = GlobalID */)
[51929]2088{
[75305]2089 /* Get the actual value: */
2090 QString strValue = extraDataStringUnion(strKey, uID);
2091 /* If actual value is null we might be able to find old one: */
[61191]2092 if (strValue.isNull())
[75305]2093 {
2094 foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
2095 {
2096 strValue = extraDataStringUnion(strOldKey, uID);
2097 if (!strValue.isNull())
2098 break;
2099 }
2100 }
2101 /* Return null string if result is empty: */
2102 if (strValue.isEmpty())
[51929]2103 return QString();
2104
2105 /* Returns corresponding value: */
[61191]2106 return strValue;
[51929]2107}
2108
[74942]2109void UIExtraDataManager::setExtraDataString(const QString &strKey, const QString &strValue, const QUuid &uID /* = GlobalID */)
[51929]2110{
[54151]2111 /* Make sure VBoxSVC is available: */
[79365]2112 if (!uiCommon().isVBoxSVCAvailable())
[54151]2113 return;
2114
[51929]2115 /* Hot-load machine extra-data map if necessary: */
[74942]2116 if (uID != GlobalID && !m_data.contains(uID))
2117 hotloadMachineExtraDataMap(uID);
[51929]2118
2119 /* Access corresponding map: */
[74942]2120 ExtraDataMap &data = m_data[uID];
[51929]2121
2122 /* [Re]cache passed value: */
2123 data[strKey] = strValue;
2124
2125 /* Global extra-data: */
[74942]2126 if (uID == GlobalID)
[51929]2127 {
2128 /* Get global object: */
[79365]2129 CVirtualBox comVBox = uiCommon().virtualBox();
[51929]2130 /* Update global extra-data: */
[75304]2131 comVBox.SetExtraData(strKey, strValue);
2132 if (!comVBox.isOk())
2133 msgCenter().cannotSetExtraData(comVBox, strKey, strValue);
[75305]2134 /* Wipe out old keys: */
2135 foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
2136 {
2137 comVBox.SetExtraData(strOldKey, QString());
2138 if (!comVBox.isOk())
2139 {
2140 msgCenter().cannotSetExtraData(comVBox, strOldKey, strValue);
2141 break;
2142 }
2143 }
[51929]2144 }
2145 /* Machine extra-data: */
2146 else
2147 {
2148 /* Search for corresponding machine: */
[79365]2149 CVirtualBox comVBox = uiCommon().virtualBox();
[75304]2150 const CMachine comMachine = comVBox.FindMachine(uID.toString());
2151 AssertReturnVoid(comVBox.isOk() && !comMachine.isNull());
[54794]2152 /* Check the configuration access-level: */
[75304]2153 const KMachineState enmMachineState = comMachine.GetState();
2154 const KSessionState enmSessionState = comMachine.GetSessionState();
2155 const ConfigurationAccessLevel enmLevel = configurationAccessLevel(enmSessionState, enmMachineState);
[54794]2156 /* Prepare machine session: */
[75304]2157 CSession comSession;
2158 if (enmLevel == ConfigurationAccessLevel_Full)
[79365]2159 comSession = uiCommon().openSession(uID);
[54794]2160 else
[79365]2161 comSession = uiCommon().openExistingSession(uID);
[75304]2162 AssertReturnVoid(!comSession.isNull());
[54794]2163 /* Get machine from that session: */
[75304]2164 CMachine comSessionMachine = comSession.GetMachine();
[51929]2165 /* Update machine extra-data: */
[75304]2166 comSessionMachine.SetExtraData(strKey, strValue);
2167 if (!comSessionMachine.isOk())
2168 msgCenter().cannotSetExtraData(comSessionMachine, strKey, strValue);
[75305]2169 /* Wipe out old keys: */
2170 foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
2171 {
2172 comSessionMachine.SetExtraData(strOldKey, QString());
2173 if (!comSessionMachine.isOk())
2174 {
2175 msgCenter().cannotSetExtraData(comSessionMachine, strOldKey, strValue);
2176 break;
2177 }
2178 }
[75304]2179 comSession.UnlockMachine();
[51929]2180 }
2181}
2182
[74942]2183QStringList UIExtraDataManager::extraDataStringList(const QString &strKey, const QUuid &uID /* = GlobalID */)
[51929]2184{
[75305]2185 /* Get the actual value: */
2186 QString strValue = extraDataStringUnion(strKey, uID);
2187 /* If actual value is null we might be able to find old one: */
[61191]2188 if (strValue.isNull())
[75305]2189 {
2190 foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
2191 {
2192 strValue = extraDataStringUnion(strOldKey, uID);
2193 if (!strValue.isNull())
2194 break;
2195 }
2196 }
2197 /* Return empty string list if result is empty: */
2198 if (strValue.isEmpty())
[51929]2199 return QStringList();
2200
2201 /* Few old extra-data string-lists were separated with 'semicolon' symbol.
2202 * All new separated by 'comma'. We have to take that into account. */
[93982]2203#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
[93996]2204 return strValue.split(QRegularExpression("[;,]"), Qt::SkipEmptyParts);
[93982]2205#else
[93996]2206 return strValue.split(QRegularExpression("[;,]"), QString::SkipEmptyParts);
[93982]2207#endif
[51929]2208}
2209
[74942]2210void UIExtraDataManager::setExtraDataStringList(const QString &strKey, const QStringList &value, const QUuid &uID /* = GlobalID */)
[51929]2211{
[54151]2212 /* Make sure VBoxSVC is available: */
[79365]2213 if (!uiCommon().isVBoxSVCAvailable())
[54151]2214 return;
2215
[51929]2216 /* Hot-load machine extra-data map if necessary: */
[74942]2217 if (uID != GlobalID && !m_data.contains(uID))
2218 hotloadMachineExtraDataMap(uID);
[51929]2219
2220 /* Access corresponding map: */
[74942]2221 ExtraDataMap &data = m_data[uID];
[51929]2222
2223 /* [Re]cache passed value: */
[54794]2224 data[strKey] = value.join(",");
[51929]2225
2226 /* Global extra-data: */
[74942]2227 if (uID == GlobalID)
[51929]2228 {
2229 /* Get global object: */
[79365]2230 CVirtualBox comVBox = uiCommon().virtualBox();
[51929]2231 /* Update global extra-data: */
[75304]2232 comVBox.SetExtraDataStringList(strKey, value);
2233 if (!comVBox.isOk())
2234 msgCenter().cannotSetExtraData(comVBox, strKey, value.join(","));
[75305]2235 /* Wipe out old keys: */
2236 foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
2237 {
2238 comVBox.SetExtraData(strOldKey, QString());
2239 if (!comVBox.isOk())
2240 {
2241 msgCenter().cannotSetExtraData(comVBox, strOldKey, value.join(","));
2242 break;
2243 }
2244 }
[51929]2245 }
2246 /* Machine extra-data: */
2247 else
2248 {
2249 /* Search for corresponding machine: */
[79365]2250 CVirtualBox comVBox = uiCommon().virtualBox();
[75304]2251 const CMachine comMachine = comVBox.FindMachine(uID.toString());
2252 AssertReturnVoid(comVBox.isOk() && !comMachine.isNull());
[54794]2253 /* Check the configuration access-level: */
[75304]2254 const KMachineState enmMachineState = comMachine.GetState();
2255 const KSessionState enmSessionState = comMachine.GetSessionState();
2256 const ConfigurationAccessLevel enmLevel = configurationAccessLevel(enmSessionState, enmMachineState);
[54794]2257 /* Prepare machine session: */
[75304]2258 CSession comSession;
2259 if (enmLevel == ConfigurationAccessLevel_Full)
[79365]2260 comSession = uiCommon().openSession(uID);
[54794]2261 else
[79365]2262 comSession = uiCommon().openExistingSession(uID);
[75304]2263 AssertReturnVoid(!comSession.isNull());
[54794]2264 /* Get machine from that session: */
[75304]2265 CMachine comSessionMachine = comSession.GetMachine();
[51929]2266 /* Update machine extra-data: */
[75304]2267 comSessionMachine.SetExtraDataStringList(strKey, value);
2268 if (!comSessionMachine.isOk())
2269 msgCenter().cannotSetExtraData(comSessionMachine, strKey, value.join(","));
[75305]2270 /* Wipe out old keys: */
2271 foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
2272 {
2273 comSessionMachine.SetExtraData(strOldKey, QString());
2274 if (!comSessionMachine.isOk())
2275 {
2276 msgCenter().cannotSetExtraData(comSessionMachine, strOldKey, value.join(","));
2277 break;
2278 }
2279 }
[75304]2280 comSession.UnlockMachine();
[51929]2281 }
2282}
2283
[51056]2284UIExtraDataManager::UIExtraDataManager()
[51174]2285 : m_pHandler(0)
[51056]2286{
2287 /* Connect to static instance: */
[71580]2288 s_pInstance = this;
[51056]2289}
2290
2291UIExtraDataManager::~UIExtraDataManager()
2292{
2293 /* Disconnect from static instance: */
[71580]2294 s_pInstance = 0;
[51056]2295}
2296
[81612]2297UIExtraDataMetaDefs::DialogType UIExtraDataManager::restrictedDialogTypes(const QUuid &uID)
2298{
2299 /* Prepare result: */
2300 UIExtraDataMetaDefs::DialogType result = UIExtraDataMetaDefs::DialogType_Invalid;
2301 /* Get restricted runtime-menu-types: */
2302 foreach (const QString &strValue, extraDataStringList(GUI_RestrictedDialogs, uID))
2303 {
2304 UIExtraDataMetaDefs::DialogType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::DialogType>(strValue);
2305 if (value != UIExtraDataMetaDefs::DialogType_Invalid)
2306 result = static_cast<UIExtraDataMetaDefs::DialogType>(result | value);
2307 }
2308 /* Return result: */
2309 return result;
2310}
2311
2312void UIExtraDataManager::setRestrictedDialogTypes(UIExtraDataMetaDefs::DialogType dialogs, const QUuid &uID)
2313{
2314 /* We have MenuType enum registered, so we can enumerate it: */
2315 const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
2316 const int iEnumIndex = smo.indexOfEnumerator("DialogType");
2317 QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
2318
2319 /* Prepare result: */
2320 QStringList result;
2321 /* Handle DialogType_All enum-value: */
2322 if (dialogs == UIExtraDataMetaDefs::DialogType_All)
2323 result << gpConverter->toInternalString(dialogs);
2324 else
2325 {
2326 /* Handle other enum-values: */
2327 for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
2328 {
2329 /* Get iterated enum-value: */
2330 const UIExtraDataMetaDefs::DialogType enumValue =
2331 static_cast<UIExtraDataMetaDefs::DialogType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
2332 /* Skip DialogType_Invalid & DialogType_All enum-values: */
2333 if (enumValue == UIExtraDataMetaDefs::DialogType_Invalid ||
2334 enumValue == UIExtraDataMetaDefs::DialogType_All)
2335 continue;
2336 if (dialogs & enumValue)
2337 result << gpConverter->toInternalString(enumValue);
2338 }
2339 }
2340 /* Save result: */
2341 setExtraDataStringList(GUI_RestrictedDialogs, result, uID);
2342}
2343
[89295]2344UIColorThemeType UIExtraDataManager::colorTheme()
2345{
2346 return gpConverter->fromInternalString<UIColorThemeType>(extraDataString(GUI_ColorTheme));
2347}
2348
2349void UIExtraDataManager::setColorTheme(const UIColorThemeType &enmType)
2350{
2351 setExtraDataString(GUI_ColorTheme, gpConverter->toInternalString(enmType));
2352}
2353
[74942]2354QStringList UIExtraDataManager::suppressedMessages(const QUuid &uID /* = GlobalID */)
[51195]2355{
[74942]2356 return extraDataStringList(GUI_SuppressMessages, uID);
[51679]2357}
2358
2359void UIExtraDataManager::setSuppressedMessages(const QStringList &list)
2360{
2361 setExtraDataStringList(GUI_SuppressMessages, list);
2362}
2363
[51928]2364QStringList UIExtraDataManager::messagesWithInvertedOption()
[51679]2365{
2366 return extraDataStringList(GUI_InvertMessageOption);
2367}
2368
[99184]2369#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
[91227]2370bool UIExtraDataManager::keepSuccessfullNotificationProgresses()
2371{
2372 /* 'False' unless feature allowed: */
[92398]2373 return isFeatureAllowed(GUI_NotificationCenter_KeepSuccessfullProgresses);
[91227]2374}
2375
[91236]2376void UIExtraDataManager::setKeepSuccessfullNotificationProgresses(bool fKeep)
2377{
2378 /* 'True' if feature allowed, null-string otherwise: */
[92398]2379 setExtraDataString(GUI_NotificationCenter_KeepSuccessfullProgresses, toFeatureAllowed(fKeep));
[91236]2380}
[99184]2381#endif /* VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON */
[91236]2382
[93408]2383Qt::Alignment UIExtraDataManager::notificationCenterAlignment()
2384{
2385 const QString strValue = extraDataString(GUI_NotificationCenter_Alignment);
2386 return strValue.isEmpty() ? Qt::AlignTop : gpConverter->fromInternalString<Qt::Alignment>(strValue);
2387}
2388
2389void UIExtraDataManager::setNotificationCenterAlignment(Qt::Alignment enmOrder)
2390{
2391 const QString strValue = enmOrder == Qt::AlignTop ? QString() : gpConverter->toInternalString(enmOrder);
2392 setExtraDataString(GUI_NotificationCenter_Alignment, strValue);
2393}
2394
[92400]2395Qt::SortOrder UIExtraDataManager::notificationCenterOrder()
2396{
[92401]2397 const QString strValue = extraDataString(GUI_NotificationCenter_Order);
2398 return strValue.isEmpty() ? Qt::DescendingOrder : gpConverter->fromInternalString<Qt::SortOrder>(strValue);
[92400]2399}
2400
2401void UIExtraDataManager::setNotificationCenterOrder(Qt::SortOrder enmOrder)
2402{
[92401]2403 const QString strValue = enmOrder == Qt::DescendingOrder ? QString() : gpConverter->toInternalString(enmOrder);
[92400]2404 setExtraDataString(GUI_NotificationCenter_Order, strValue);
2405}
2406
[96808]2407bool UIExtraDataManager::preventBetaBuildLavel()
2408{
2409 return isFeatureAllowed(GUI_PreventBetaLabel);
2410}
2411
[51679]2412#if !defined(VBOX_BLEEDING_EDGE) && !defined(DEBUG)
[51928]2413QString UIExtraDataManager::preventBetaBuildWarningForVersion()
[51679]2414{
[51195]2415 return extraDataString(GUI_PreventBetaWarning);
2416}
[51679]2417#endif /* !defined(VBOX_BLEEDING_EDGE) && !defined(DEBUG) */
[51195]2418
[51216]2419#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
[51928]2420bool UIExtraDataManager::applicationUpdateEnabled()
[51447]2421{
[51679]2422 /* 'True' unless 'restriction' feature allowed: */
[51447]2423 return !isFeatureAllowed(GUI_PreventApplicationUpdate);
2424}
2425
[51928]2426QString UIExtraDataManager::applicationUpdateData()
[51216]2427{
2428 return extraDataString(GUI_UpdateDate);
2429}
2430
2431void UIExtraDataManager::setApplicationUpdateData(const QString &strValue)
2432{
2433 setExtraDataString(GUI_UpdateDate, strValue);
2434}
2435
[51928]2436qulonglong UIExtraDataManager::applicationUpdateCheckCounter()
[51216]2437{
2438 /* Read subsequent update check counter value: */
2439 qulonglong uResult = 1;
2440 const QString strCheckCount = extraDataString(GUI_UpdateCheckCount);
2441 if (!strCheckCount.isEmpty())
2442 {
2443 bool ok = false;
[51679]2444 qulonglong uCheckCount = strCheckCount.toULongLong(&ok);
[51216]2445 if (ok) uResult = uCheckCount;
2446 }
2447 /* Return update check counter value: */
2448 return uResult;
2449}
2450
2451void UIExtraDataManager::incrementApplicationUpdateCheckCounter()
2452{
2453 /* Increment update check counter value: */
2454 setExtraDataString(GUI_UpdateCheckCount, QString::number(applicationUpdateCheckCounter() + 1));
2455}
2456#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
2457
[69006]2458bool UIExtraDataManager::legacyProgressHandlingRequested()
2459{
[69133]2460 /* 'False' unless feature allowed: */
2461 return isFeatureAllowed(GUI_Progress_LegacyMode);
[69006]2462}
2463
[66579]2464bool UIExtraDataManager::guiFeatureEnabled(GUIFeatureType enmFeature)
2465{
2466 /* Acquire GUI feature list: */
2467 GUIFeatureType enmFeatures = GUIFeatureType_None;
2468 foreach (const QString &strValue, extraDataStringList(GUI_Customizations))
2469 enmFeatures = static_cast<GUIFeatureType>(enmFeatures | gpConverter->fromInternalString<GUIFeatureType>(strValue));
2470 /* Return whether the requested feature is enabled: */
2471 return enmFeatures & enmFeature;
2472}
2473
[51928]2474QList<GlobalSettingsPageType> UIExtraDataManager::restrictedGlobalSettingsPages()
[51679]2475{
2476 /* Prepare result: */
2477 QList<GlobalSettingsPageType> result;
2478 /* Get restricted global-settings-pages: */
2479 foreach (const QString &strValue, extraDataStringList(GUI_RestrictedGlobalSettingsPages))
2480 {
2481 GlobalSettingsPageType value = gpConverter->fromInternalString<GlobalSettingsPageType>(strValue);
2482 if (value != GlobalSettingsPageType_Invalid)
2483 result << value;
2484 }
2485 /* Return result: */
2486 return result;
2487}
2488
[74942]2489QList<MachineSettingsPageType> UIExtraDataManager::restrictedMachineSettingsPages(const QUuid &uID)
[51679]2490{
2491 /* Prepare result: */
2492 QList<MachineSettingsPageType> result;
2493 /* Get restricted machine-settings-pages: */
[74942]2494 foreach (const QString &strValue, extraDataStringList(GUI_RestrictedMachineSettingsPages, uID))
[51679]2495 {
2496 MachineSettingsPageType value = gpConverter->fromInternalString<MachineSettingsPageType>(strValue);
2497 if (value != MachineSettingsPageType_Invalid)
2498 result << value;
2499 }
2500 /* Return result: */
2501 return result;
2502}
2503
[101447]2504bool UIExtraDataManager::isSettingsInExpertMode()
2505{
2506#ifdef DEBUG
2507 /* For debug build return true by default: */
2508 if (extraDataString(GUI_Settings_ExpertMode).isEmpty())
2509 return true;
2510#endif
2511
2512 /* 'False' unless feature allowed: */
2513 return isFeatureAllowed(GUI_Settings_ExpertMode);
2514}
2515
2516void UIExtraDataManager::setSettingsInExpertMode(bool fExpertMode)
2517{
2518 /* Store actual feature state, whether it is "true" or "false",
2519 * because absent state means default, depending on defines: */
2520 setExtraDataString(GUI_Settings_ExpertMode, toFeatureState(fExpertMode));
2521}
2522
[66587]2523QString UIExtraDataManager::languageId()
2524{
2525 /* Load language ID: */
2526 return extraDataString(GUI_LanguageID);
2527}
2528
2529void UIExtraDataManager::setLanguageId(const QString &strLanguageId)
2530{
2531 /* Save language ID: */
2532 setExtraDataString(GUI_LanguageID, strLanguageId);
2533}
2534
[86977]2535MaximumGuestScreenSizePolicy UIExtraDataManager::maxGuestResolutionPolicy()
[66588]2536{
2537 /* Return maximum guest-screen resolution policy: */
[86977]2538 return gpConverter->fromInternalString<MaximumGuestScreenSizePolicy>(extraDataString(GUI_MaxGuestResolution));
[66588]2539}
2540
[86977]2541void UIExtraDataManager::setMaxGuestScreenResolution(MaximumGuestScreenSizePolicy enmPolicy, const QSize resolution /* = QSize() */)
[66588]2542{
2543 /* If policy is 'Fixed' => call the wrapper: */
[86977]2544 if (enmPolicy == MaximumGuestScreenSizePolicy_Fixed)
[66588]2545 setMaxGuestResolutionForPolicyFixed(resolution);
2546 /* Otherwise => just store the value: */
2547 else
2548 setExtraDataString(GUI_MaxGuestResolution, gpConverter->toInternalString(enmPolicy));
2549}
2550
2551QSize UIExtraDataManager::maxGuestResolutionForPolicyFixed()
2552{
2553 /* Acquire maximum guest-screen resolution policy: */
2554 const QString strPolicy = extraDataString(GUI_MaxGuestResolution);
[86977]2555 const MaximumGuestScreenSizePolicy enmPolicy = gpConverter->fromInternalString<MaximumGuestScreenSizePolicy>(strPolicy);
[66588]2556
2557 /* Make sure maximum guest-screen resolution policy is really Fixed: */
[86977]2558 if (enmPolicy != MaximumGuestScreenSizePolicy_Fixed)
[66588]2559 return QSize();
2560
2561 /* Parse maximum guest-screen resolution: */
2562 const QStringList values = strPolicy.split(',');
2563 int iWidth = values.at(0).toInt();
2564 int iHeight = values.at(1).toInt();
2565 if (iWidth <= 0)
2566 iWidth = 640;
2567 if (iHeight <= 0)
2568 iHeight = 480;
2569
2570 /* Return maximum guest-screen resolution: */
2571 return QSize(iWidth, iHeight);
2572}
2573
2574void UIExtraDataManager::setMaxGuestResolutionForPolicyFixed(const QSize &resolution)
2575{
2576 /* If resolution is 'empty' => call the wrapper: */
2577 if (resolution.isEmpty())
[86977]2578 setMaxGuestScreenResolution(MaximumGuestScreenSizePolicy_Automatic);
[66588]2579 /* Otherwise => just store the value: */
2580 else
2581 setExtraDataString(GUI_MaxGuestResolution, QString("%1,%2").arg(resolution.width()).arg(resolution.height()));
2582}
2583
[53249]2584bool UIExtraDataManager::activateHoveredMachineWindow()
2585{
2586 /* 'False' unless feature allowed: */
2587 return isFeatureAllowed(GUI_ActivateHoveredMachineWindow);
2588}
2589
[53250]2590void UIExtraDataManager::setActivateHoveredMachineWindow(bool fActivate)
2591{
2592 /* 'True' if feature allowed, null-string otherwise: */
2593 setExtraDataString(GUI_ActivateHoveredMachineWindow, toFeatureAllowed(fActivate));
2594}
2595
[89688]2596bool UIExtraDataManager::disableHostScreenSaver()
2597{
2598 /* 'False' unless feature allowed: */
2599 return isFeatureAllowed(GUI_DisableHostScreenSaver);
2600}
2601
2602void UIExtraDataManager::setDisableHostScreenSaver(bool fActivate)
2603{
2604 /* 'True' if feature allowed, null-string otherwise: */
2605 setExtraDataString(GUI_DisableHostScreenSaver, toFeatureAllowed(fActivate));
2606}
2607
[66569]2608QString UIExtraDataManager::hostKeyCombination()
2609{
2610 /* Acquire host-key combination: */
2611 QString strHostCombo = extraDataString(GUI_Input_HostKeyCombination);
2612 /* Invent some sane default if it's absolutely wrong or invalid: */
2613 QRegularExpression reTemplate("0|[1-9]\\d*(,[1-9]\\d*)?(,[1-9]\\d*)?");
2614 if (!reTemplate.match(strHostCombo).hasMatch() || !UIHostCombo::isValidKeyCombo(strHostCombo))
2615 {
2616#if defined (VBOX_WS_MAC)
2617 strHostCombo = "55"; // QZ_LMETA
2618#elif defined (VBOX_WS_WIN)
2619 strHostCombo = "163"; // VK_RCONTROL
[100064]2620#elif defined (VBOX_WS_NIX)
[66569]2621 strHostCombo = "65508"; // XK_Control_R
2622#else
2623# warning "port me!"
2624#endif
2625 }
[66571]2626 /* Return host-key combination: */
[66569]2627 return strHostCombo;
2628}
2629
[97307]2630void UIExtraDataManager::setFontScaleFactor(int iFontScaleFactor)
2631{
[97311]2632 if (iFontScaleFactor < UIExtraDataDefs::iFontScaleMin || iFontScaleFactor > UIExtraDataDefs::iFontScaleMax)
[97307]2633 return;
2634 setExtraDataString(GUI_FontScaleFactor, QString::number(iFontScaleFactor));
2635}
2636
2637int UIExtraDataManager::fontScaleFactor()
2638{
2639 QString strFontScaleFactor = extraDataString(GUI_FontScaleFactor);
2640 bool fConversion = false;
2641 int iScaleFactor = strFontScaleFactor.toInt(&fConversion);
[97311]2642 if (!fConversion || iScaleFactor < UIExtraDataDefs::iFontScaleMin || iScaleFactor > UIExtraDataDefs::iFontScaleMax)
[97307]2643 return 100;
2644 return iScaleFactor;
2645}
2646
[66569]2647void UIExtraDataManager::setHostKeyCombination(const QString &strHostCombo)
2648{
2649 /* Do not save anything if it's absolutely wrong or invalid: */
2650 QRegularExpression reTemplate("0|[1-9]\\d*(,[1-9]\\d*)?(,[1-9]\\d*)?");
2651 if (!reTemplate.match(strHostCombo).hasMatch() || !UIHostCombo::isValidKeyCombo(strHostCombo))
2652 return;
2653 /* Define host-combo: */
2654 setExtraDataString(GUI_Input_HostKeyCombination, strHostCombo);
2655}
2656
[51928]2657QStringList UIExtraDataManager::shortcutOverrides(const QString &strPoolExtraDataID)
[51449]2658{
2659 if (strPoolExtraDataID == GUI_Input_SelectorShortcuts)
2660 return extraDataStringList(GUI_Input_SelectorShortcuts);
2661 if (strPoolExtraDataID == GUI_Input_MachineShortcuts)
2662 return extraDataStringList(GUI_Input_MachineShortcuts);
2663 return QStringList();
2664}
2665
[66571]2666bool UIExtraDataManager::autoCaptureEnabled()
2667{
2668 /* Prepare auto-capture flag: */
2669 bool fAutoCapture = true /* indifferently */;
2670 /* Acquire whether the auto-capture is restricted: */
2671 QString strAutoCapture = extraDataString(GUI_Input_AutoCapture);
2672 /* Invent some sane default if it's empty: */
2673 if (strAutoCapture.isEmpty())
2674 {
[100064]2675#if defined(VBOX_WS_NIX) && defined(DEBUG)
[66571]2676 fAutoCapture = false;
2677#else
2678 fAutoCapture = true;
2679#endif
2680 }
2681 /* 'True' unless feature restricted: */
2682 else
2683 fAutoCapture = !isFeatureRestricted(GUI_Input_AutoCapture);
2684 /* Return auto-capture flag: */
2685 return fAutoCapture;
2686}
2687
2688void UIExtraDataManager::setAutoCaptureEnabled(bool fEnabled)
2689{
2690 /* Store actual feature state, whether it is "true" or "false",
2691 * because absent state means default, different on various hosts: */
2692 setExtraDataString(GUI_Input_AutoCapture, toFeatureState(fEnabled));
2693}
2694
[66589]2695QString UIExtraDataManager::remappedScanCodes()
2696{
2697 /* Acquire remapped scan codes: */
2698 QString strRemappedScanCodes = extraDataString(GUI_RemapScancodes);
2699 /* Clear the record if it's absolutely wrong: */
2700 QRegularExpression reTemplate("(\\d+=\\d+,)*\\d+=\\d+");
2701 if (!reTemplate.match(strRemappedScanCodes).hasMatch())
2702 strRemappedScanCodes.clear();
2703 /* Return remapped scan codes: */
2704 return strRemappedScanCodes;
2705}
2706
[66590]2707QString UIExtraDataManager::proxySettings()
2708{
2709 return extraDataString(GUI_ProxySettings);
2710}
2711
2712void UIExtraDataManager::setProxySettings(const QString &strSettings)
2713{
2714 setExtraDataString(GUI_ProxySettings, strSettings);
2715}
2716
[51928]2717QString UIExtraDataManager::recentFolderForHardDrives()
[51198]2718{
2719 return extraDataString(GUI_RecentFolderHD);
2720}
2721
[51928]2722QString UIExtraDataManager::recentFolderForOpticalDisks()
[51198]2723{
2724 return extraDataString(GUI_RecentFolderCD);
2725}
2726
[51928]2727QString UIExtraDataManager::recentFolderForFloppyDisks()
[51198]2728{
2729 return extraDataString(GUI_RecentFolderFD);
2730}
2731
2732void UIExtraDataManager::setRecentFolderForHardDrives(const QString &strValue)
2733{
2734 setExtraDataString(GUI_RecentFolderHD, strValue);
2735}
2736
2737void UIExtraDataManager::setRecentFolderForOpticalDisks(const QString &strValue)
2738{
2739 setExtraDataString(GUI_RecentFolderCD, strValue);
2740}
2741
2742void UIExtraDataManager::setRecentFolderForFloppyDisks(const QString &strValue)
2743{
2744 setExtraDataString(GUI_RecentFolderFD, strValue);
2745}
2746
[51928]2747QStringList UIExtraDataManager::recentListOfHardDrives()
[51198]2748{
2749 return extraDataStringList(GUI_RecentListHD);
2750}
2751
[51928]2752QStringList UIExtraDataManager::recentListOfOpticalDisks()
[51198]2753{
2754 return extraDataStringList(GUI_RecentListCD);
2755}
2756
[51928]2757QStringList UIExtraDataManager::recentListOfFloppyDisks()
[51198]2758{
2759 return extraDataStringList(GUI_RecentListFD);
2760}
2761
[51199]2762void UIExtraDataManager::setRecentListOfHardDrives(const QStringList &value)
[51198]2763{
[51199]2764 setExtraDataStringList(GUI_RecentListHD, value);
[51198]2765}
2766
[51199]2767void UIExtraDataManager::setRecentListOfOpticalDisks(const QStringList &value)
[51198]2768{
[51199]2769 setExtraDataStringList(GUI_RecentListCD, value);
[51198]2770}
2771
[51199]2772void UIExtraDataManager::setRecentListOfFloppyDisks(const QStringList &value)
[51198]2773{
[51199]2774 setExtraDataStringList(GUI_RecentListFD, value);
[51198]2775}
2776
[78519]2777UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork UIExtraDataManager::restrictedNetworkAttachmentTypes()
2778{
2779 /* Prepare result: */
2780 UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmResult =
2781 UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid;
2782 /* Get restricted network attachment types: */
2783 foreach (const QString &strValue, extraDataStringList(GUI_RestrictedNetworkAttachmentTypes))
2784 {
2785 const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmValue =
2786 gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(strValue);
2787 if (enmValue != UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid && !(enmResult & enmValue))
2788 enmResult = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(enmResult | enmValue);
2789 }
2790 /* Return result: */
2791 return enmResult;
2792}
2793
[77376]2794QString UIExtraDataManager::visoCreatorRecentFolder()
2795{
[77377]2796 return extraDataString(GUI_VISOCreator_RecentFolder);
[77376]2797}
2798
[77377]2799void UIExtraDataManager::setVISOCreatorRecentFolder(const QString &strValue)
[77376]2800{
[77377]2801 setExtraDataString(GUI_VISOCreator_RecentFolder, strValue);
[77376]2802}
2803
[92492]2804QRect UIExtraDataManager::visoCreatorDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry)
2805{
2806 return dialogGeometry(GUI_VISOCreator_DialogGeometry, pWidget, pParentWidget, defaultGeometry);
2807}
2808
2809void UIExtraDataManager::setVisoCreatorDialogGeometry(const QRect &geometry, bool fMaximized)
2810{
2811 setDialogGeometry(GUI_VISOCreator_DialogGeometry, geometry, fMaximized);
2812}
2813
2814bool UIExtraDataManager::visoCreatorDialogShouldBeMaximized()
2815{
[92496]2816 return dialogShouldBeMaximized(GUI_VISOCreator_DialogGeometry);
[92492]2817}
2818
[51928]2819QRect UIExtraDataManager::selectorWindowGeometry(QWidget *pWidget)
[51455]2820{
[81358]2821 return dialogGeometry(GUI_LastSelectorWindowPosition, pWidget);
[51455]2822}
2823
[51928]2824bool UIExtraDataManager::selectorWindowShouldBeMaximized()
[51455]2825{
[92496]2826 return dialogShouldBeMaximized(GUI_LastSelectorWindowPosition);
[51455]2827}
2828
2829void UIExtraDataManager::setSelectorWindowGeometry(const QRect &geometry, bool fMaximized)
2830{
2831 /* Serialize passed values: */
2832 QStringList data;
2833 data << QString::number(geometry.x());
2834 data << QString::number(geometry.y());
2835 data << QString::number(geometry.width());
2836 data << QString::number(geometry.height());
2837 if (fMaximized)
[51665]2838 data << GUI_Geometry_State_Max;
[51455]2839
2840 /* Re-cache corresponding extra-data: */
2841 setExtraDataStringList(GUI_LastSelectorWindowPosition, data);
2842}
2843
[51928]2844QList<int> UIExtraDataManager::selectorWindowSplitterHints()
[51514]2845{
[51679]2846 /* Get corresponding extra-data: */
[51514]2847 const QStringList data = extraDataStringList(GUI_SplitterSizes);
2848
2849 /* Parse loaded data: */
2850 QList<int> hints;
2851 hints << (data.size() > 0 ? data[0].toInt() : 0);
2852 hints << (data.size() > 1 ? data[1].toInt() : 0);
2853
2854 /* Return hints: */
2855 return hints;
2856}
2857
2858void UIExtraDataManager::setSelectorWindowSplitterHints(const QList<int> &hints)
2859{
2860 /* Parse passed hints: */
2861 QStringList data;
2862 data << (hints.size() > 0 ? QString::number(hints[0]) : QString());
2863 data << (hints.size() > 1 ? QString::number(hints[1]) : QString());
2864
2865 /* Re-cache corresponding extra-data: */
2866 setExtraDataStringList(GUI_SplitterSizes, data);
2867}
2868
[51928]2869bool UIExtraDataManager::selectorWindowToolBarVisible()
[51515]2870{
[51595]2871 /* 'True' unless feature restricted: */
[51515]2872 return !isFeatureRestricted(GUI_Toolbar);
2873}
2874
2875void UIExtraDataManager::setSelectorWindowToolBarVisible(bool fVisible)
2876{
[51679]2877 /* 'False' if feature restricted, null-string otherwise: */
[51515]2878 setExtraDataString(GUI_Toolbar, toFeatureRestricted(!fVisible));
2879}
2880
[68212]2881bool UIExtraDataManager::selectorWindowToolBarTextVisible()
2882{
2883 /* 'True' unless feature restricted: */
2884 return !isFeatureRestricted(GUI_Toolbar_Text);
2885}
2886
2887void UIExtraDataManager::setSelectorWindowToolBarTextVisible(bool fVisible)
2888{
2889 /* 'False' if feature restricted, null-string otherwise: */
2890 setExtraDataString(GUI_Toolbar_Text, toFeatureRestricted(!fVisible));
2891}
2892
[75017]2893QList<UIToolType> UIExtraDataManager::toolsPaneLastItemsChosen()
[74265]2894{
[75017]2895 /* Parse loaded data: */
2896 QList<UIToolType> result;
2897 foreach (const QString &strValue, extraDataStringList(GUI_Tools_LastItemsSelected))
2898 {
2899 const UIToolType enmType = gpConverter->fromInternalString<UIToolType>(strValue);
2900 if (enmType != UIToolType_Invalid)
2901 result << enmType;
2902 }
2903
2904 /* Return result: */
2905 return result;
[74265]2906}
2907
[75017]2908void UIExtraDataManager::setToolsPaneLastItemsChosen(const QList<UIToolType> &set)
[74265]2909{
[75017]2910 /* Serialize passed values: */
2911 QStringList data;
2912 foreach (const UIToolType &enmType, set)
2913 data << gpConverter->toInternalString(enmType);
2914
2915 /* Re-cache corresponding extra-data: */
2916 setExtraDataStringList(GUI_Tools_LastItemsSelected, data);
[74265]2917}
2918
[51928]2919bool UIExtraDataManager::selectorWindowStatusBarVisible()
[51515]2920{
[51595]2921 /* 'True' unless feature restricted: */
[51515]2922 return !isFeatureRestricted(GUI_Statusbar);
2923}
2924
2925void UIExtraDataManager::setSelectorWindowStatusBarVisible(bool fVisible)
2926{
[51679]2927 /* 'False' if feature restricted, null-string otherwise: */
[51515]2928 setExtraDataString(GUI_Statusbar, toFeatureRestricted(!fVisible));
2929}
2930
[88585]2931QStringList UIExtraDataManager::knownMachineGroupDefinitionKeys()
[51548]2932{
[88585]2933 /* Acquire a list of known group definition keys: */
2934 QStringList result;
[51928]2935 foreach (const QString &strKey, m_data.value(GlobalID).keys())
[51548]2936 if (strKey.startsWith(GUI_GroupDefinitions))
[88585]2937 {
2938 QString strGroupID = strKey;
2939 strGroupID.remove(GUI_GroupDefinitions);
2940 result << strGroupID;
2941 }
2942
2943 /* Return result: */
2944 return result;
[51548]2945}
2946
[88585]2947QStringList UIExtraDataManager::machineGroupDefinitions(const QString &strGroupID)
[51548]2948{
2949 return extraDataStringList(GUI_GroupDefinitions + strGroupID);
2950}
2951
[88585]2952void UIExtraDataManager::setMachineGroupDefinitions(const QString &strGroupID, const QStringList &definitions)
[51548]2953{
2954 setExtraDataStringList(GUI_GroupDefinitions + strGroupID, definitions);
2955}
2956
[51928]2957QString UIExtraDataManager::selectorWindowLastItemChosen()
[51541]2958{
2959 return extraDataString(GUI_LastItemSelected);
2960}
2961
2962void UIExtraDataManager::setSelectorWindowLastItemChosen(const QString &strItemID)
2963{
2964 setExtraDataString(GUI_LastItemSelected, strItemID);
2965}
2966
[51532]2967QMap<DetailsElementType, bool> UIExtraDataManager::selectorWindowDetailsElements()
2968{
[51679]2969 /* Get corresponding extra-data: */
[75306]2970 const QStringList data = extraDataStringList(GUI_Details_Elements);
[51532]2971
[51679]2972 /* Desearialize passed elements: */
[51532]2973 QMap<DetailsElementType, bool> elements;
2974 foreach (QString strItem, data)
2975 {
2976 bool fOpened = true;
2977 if (strItem.endsWith("Closed", Qt::CaseInsensitive))
2978 {
2979 fOpened = false;
[75306]2980 strItem.remove("Closed", Qt::CaseInsensitive);
[51532]2981 }
[75306]2982 const DetailsElementType enmType = gpConverter->fromInternalString<DetailsElementType>(strItem);
2983 if (enmType != DetailsElementType_Invalid)
2984 elements[enmType] = fOpened;
[51532]2985 }
2986
[75524]2987 /* If settings are empty: */
2988 if (elements.isEmpty())
2989 {
2990 /* Propose the defaults: */
2991 elements[DetailsElementType_General] = true;
2992 elements[DetailsElementType_Preview] = true;
2993 elements[DetailsElementType_System] = true;
2994 elements[DetailsElementType_Display] = true;
2995 elements[DetailsElementType_Storage] = true;
2996 elements[DetailsElementType_Audio] = true;
2997 elements[DetailsElementType_Network] = true;
2998 elements[DetailsElementType_USB] = true;
2999 elements[DetailsElementType_SF] = true;
3000 elements[DetailsElementType_Description] = true;
3001 }
3002
[51532]3003 /* Return elements: */
3004 return elements;
3005}
3006
3007void UIExtraDataManager::setSelectorWindowDetailsElements(const QMap<DetailsElementType, bool> &elements)
3008{
3009 /* Prepare corresponding extra-data: */
3010 QStringList data;
3011
3012 /* Searialize passed elements: */
[75306]3013 foreach (DetailsElementType enmType, elements.keys())
[51532]3014 {
[75306]3015 QString strValue = gpConverter->toInternalString(enmType);
3016 if (!elements[enmType])
[51532]3017 strValue += "Closed";
3018 data << strValue;
3019 }
3020
[51679]3021 /* Re-cache corresponding extra-data: */
[75306]3022 setExtraDataStringList(GUI_Details_Elements, data);
[51532]3023}
3024
[51928]3025PreviewUpdateIntervalType UIExtraDataManager::selectorWindowPreviewUpdateInterval()
[51531]3026{
[75306]3027 return gpConverter->fromInternalString<PreviewUpdateIntervalType>(extraDataString(GUI_Details_Elements_Preview_UpdateInterval));
[51531]3028}
3029
3030void UIExtraDataManager::setSelectorWindowPreviewUpdateInterval(PreviewUpdateIntervalType interval)
3031{
[75306]3032 setExtraDataString(GUI_Details_Elements_Preview_UpdateInterval, gpConverter->toInternalString(interval));
[51531]3033}
3034
[75427]3035QStringList UIExtraDataManager::vboxManagerDetailsPaneElementOptions(DetailsElementType enmElementType)
3036{
3037 /* Compose full key from GUI_Details_Elements and enmElementType: */
3038 QString strElementType = gpConverter->toInternalString(enmElementType);
3039 AssertReturn(!strElementType.isEmpty(), QStringList());
3040 strElementType[0] = strElementType.at(0).toUpper();
3041 const QString strFullKey = QString("%1/%2").arg(GUI_Details_Elements).arg(strElementType);
3042
[75607]3043 /* Return option list: */
[75427]3044 return extraDataStringList(strFullKey);
3045}
3046
[75607]3047void UIExtraDataManager::setVBoxManagerDetailsPaneElementOptions(DetailsElementType enmElementType, const QStringList &options)
3048{
3049 /* Compose full key from GUI_Details_Elements and enmElementType: */
3050 QString strElementType = gpConverter->toInternalString(enmElementType);
3051 AssertReturnVoid(!strElementType.isEmpty());
3052 strElementType[0] = strElementType.at(0).toUpper();
3053 const QString strFullKey = QString("%1/%2").arg(GUI_Details_Elements).arg(strElementType);
3054
3055 /* Store option list: */
3056 setExtraDataStringList(strFullKey, options);
3057}
3058
[68962]3059bool UIExtraDataManager::snapshotManagerDetailsExpanded()
3060{
3061 /* 'False' unless feature allowed: */
3062 return isFeatureAllowed(GUI_SnapshotManager_Details_Expanded);
3063}
3064
3065void UIExtraDataManager::setSnapshotManagerDetailsExpanded(bool fExpanded)
3066{
3067 /* 'True' if feature allowed, null-string otherwise: */
3068 return setExtraDataString(GUI_SnapshotManager_Details_Expanded, toFeatureAllowed(fExpanded));
3069}
3070
3071bool UIExtraDataManager::virtualMediaManagerDetailsExpanded()
3072{
3073 /* 'False' unless feature allowed: */
3074 return isFeatureAllowed(GUI_VirtualMediaManager_Details_Expanded);
3075}
3076
3077void UIExtraDataManager::setVirtualMediaManagerDetailsExpanded(bool fExpanded)
3078{
3079 /* 'True' if feature allowed, null-string otherwise: */
3080 return setExtraDataString(GUI_VirtualMediaManager_Details_Expanded, toFeatureAllowed(fExpanded));
3081}
3082
[77014]3083bool UIExtraDataManager::virtualMediaManagerSearchWidgetExpanded()
3084{
3085 /* 'False' unless feature allowed: */
3086 return isFeatureAllowed(GUI_VirtualMediaManager_Search_Widget_Expanded);
3087}
3088
3089void UIExtraDataManager::setVirtualMediaManagerSearchWidgetExpanded(bool fExpanded)
3090{
3091 /* 'True' if feature allowed, null-string otherwise: */
3092 return setExtraDataString(GUI_VirtualMediaManager_Search_Widget_Expanded, toFeatureAllowed(fExpanded));
3093}
3094
[68962]3095bool UIExtraDataManager::hostNetworkManagerDetailsExpanded()
3096{
3097 /* 'False' unless feature allowed: */
3098 return isFeatureAllowed(GUI_HostNetworkManager_Details_Expanded);
3099}
3100
3101void UIExtraDataManager::setHostNetworkManagerDetailsExpanded(bool fExpanded)
3102{
3103 /* 'True' if feature allowed, null-string otherwise: */
3104 return setExtraDataString(GUI_HostNetworkManager_Details_Expanded, toFeatureAllowed(fExpanded));
3105}
3106
[85035]3107QStringList UIExtraDataManager::cloudProfileManagerRestrictions()
3108{
3109 return extraDataStringList(GUI_CloudProfileManager_Restrictions);
3110}
3111
3112void UIExtraDataManager::setCloudProfileManagerRestrictions(const QStringList &restrictions)
3113{
3114 return setExtraDataStringList(GUI_CloudProfileManager_Restrictions, restrictions);
3115}
3116
[74871]3117bool UIExtraDataManager::cloudProfileManagerDetailsExpanded()
3118{
3119 /* 'False' unless feature allowed: */
3120 return isFeatureAllowed(GUI_CloudProfileManager_Details_Expanded);
3121}
3122
3123void UIExtraDataManager::setCloudProfileManagerDetailsExpanded(bool fExpanded)
3124{
3125 /* 'True' if feature allowed, null-string otherwise: */
3126 return setExtraDataString(GUI_CloudProfileManager_Details_Expanded, toFeatureAllowed(fExpanded));
3127}
3128
[85389]3129QStringList UIExtraDataManager::cloudConsoleManagerApplications()
3130{
3131 /* Gather a list of keys matching required expression: */
3132 QStringList result;
3133 QRegExp re(QString("^%1/([^/]+)$").arg(GUI_CloudConsoleManager_Application));
3134 foreach (const QString &strKey, m_data.value(GlobalID).keys())
3135 if (re.indexIn(strKey) != -1)
3136 result << re.cap(1);
3137 return result;
3138}
3139
3140QStringList UIExtraDataManager::cloudConsoleManagerProfiles(const QString &strId)
3141{
3142 /* Gather a list of keys matching required expression: */
3143 QStringList result;
3144 QRegExp re(QString("^%1/%2/([^/]+)$").arg(GUI_CloudConsoleManager_Application, strId));
3145 foreach (const QString &strKey, m_data.value(GlobalID).keys())
3146 if (re.indexIn(strKey) != -1)
3147 result << re.cap(1);
3148 return result;
3149}
3150
3151QString UIExtraDataManager::cloudConsoleManagerApplication(const QString &strId)
3152{
3153 return extraDataString(QString("%1/%2").arg(GUI_CloudConsoleManager_Application, strId));
3154}
3155
3156void UIExtraDataManager::setCloudConsoleManagerApplication(const QString &strId, const QString &strDefinition)
3157{
3158 setExtraDataString(QString("%1/%2").arg(GUI_CloudConsoleManager_Application, strId), strDefinition);
3159}
3160
3161QString UIExtraDataManager::cloudConsoleManagerProfile(const QString &strApplicationId, const QString &strProfileId)
3162{
3163 return extraDataString(QString("%1/%2/%3").arg(GUI_CloudConsoleManager_Application, strApplicationId, strProfileId));
3164}
3165
3166void UIExtraDataManager::setCloudConsoleManagerProfile(const QString &strApplicationId, const QString &strProfileId, const QString &strDefinition)
3167{
3168